PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/db.mysql/src/org/netbeans/modules/db/mysql/impl/MySQLDatabaseServer.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1129 lines | 852 code | 169 blank | 108 comment | 97 complexity | 54a60e05783790007b68fce96779c66c MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2010 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.db.mysql.impl;
  45. import java.awt.Image;
  46. import java.beans.PropertyChangeEvent;
  47. import java.beans.PropertyChangeListener;
  48. import java.beans.PropertyChangeSupport;
  49. import java.net.InetAddress;
  50. import java.net.URL;
  51. import java.net.UnknownHostException;
  52. import org.netbeans.modules.db.mysql.util.DatabaseUtils;
  53. import org.netbeans.modules.db.mysql.util.Utils;
  54. import java.sql.Connection;
  55. import java.sql.PreparedStatement;
  56. import java.sql.ResultSet;
  57. import java.sql.SQLException;
  58. import java.util.ArrayList;
  59. import java.util.Collection;
  60. import java.util.HashMap;
  61. import java.util.List;
  62. import java.util.concurrent.ArrayBlockingQueue;
  63. import java.util.concurrent.BlockingQueue;
  64. import java.util.concurrent.CopyOnWriteArrayList;
  65. import java.util.concurrent.LinkedBlockingQueue;
  66. import java.util.concurrent.TimeoutException;
  67. import java.util.logging.Level;
  68. import java.util.logging.Logger;
  69. import javax.swing.SwingUtilities;
  70. import javax.swing.event.ChangeEvent;
  71. import javax.swing.event.ChangeListener;
  72. import org.netbeans.api.db.explorer.ConnectionManager;
  73. import org.netbeans.api.db.explorer.DatabaseConnection;
  74. import org.netbeans.api.db.explorer.DatabaseException;
  75. import org.netbeans.api.db.sql.support.SQLIdentifiers.Quoter;
  76. import org.netbeans.api.progress.ProgressHandle;
  77. import org.netbeans.api.progress.ProgressHandleFactory;
  78. import org.netbeans.modules.db.mysql.Database;
  79. import org.netbeans.modules.db.mysql.DatabaseServer;
  80. import org.netbeans.modules.db.mysql.DatabaseUser;
  81. import org.netbeans.modules.db.mysql.util.ExecSupport;
  82. import org.openide.awt.HtmlBrowser;
  83. import org.openide.execution.NbProcessDescriptor;
  84. import org.openide.util.Cancellable;
  85. import org.openide.util.ImageUtilities;
  86. import org.openide.util.NbBundle;
  87. import org.openide.util.RequestProcessor;
  88. import org.openide.util.Utilities;
  89. import org.openide.windows.IOProvider;
  90. import org.openide.windows.InputOutput;
  91. /**
  92. * Model for a server. Currently just uses MySQLOptions since we only
  93. * support one server, but this can be migrated to use an approach that
  94. * supports more than one server
  95. *
  96. * @author David Van Couvering
  97. */
  98. public final class MySQLDatabaseServer implements DatabaseServer, PropertyChangeListener {
  99. private static final Object lock = new Object();
  100. private static final Image ICON = ImageUtilities.loadImage("org/netbeans/modules/db/mysql/resources/catalog.gif");
  101. private static final Image ERROR_BADGE = ImageUtilities.loadImage("org/netbeans/modules/db/mysql/resources/error-badge.gif");
  102. private static boolean first = true;
  103. private volatile String displayName;
  104. private volatile String shortDescription;
  105. private volatile Image icon;
  106. private static final Logger LOGGER = Logger.getLogger(DatabaseServer.class.getName());
  107. private static InputOutput OUTPUT = null;
  108. // guarded by static variable "lock"
  109. private static volatile DatabaseServer DEFAULT;;
  110. private static final MySQLOptions OPTIONS = MySQLOptions.getDefault();
  111. // SQL commands
  112. private static final String GET_DATABASES_SQL = "SHOW DATABASES"; // NOI18N
  113. private static final String GET_USERS_SQL =
  114. "SELECT DISTINCT user, host FROM mysql.user"; // NOI18N
  115. private static final String CREATE_DATABASE_SQL = "CREATE DATABASE "; // NOI18N
  116. private static final String DROP_DATABASE_SQL = "DROP DATABASE "; // NOI18N
  117. // This is in two parts because the database name is an identifier and can't
  118. // be parameterized (it gets quoted and it is a syntax error to quote it).
  119. private static final String GRANT_ALL_SQL_1 = "GRANT ALL ON "; // NOI18N
  120. private static final String GRANT_ALL_SQL_2 = ".* TO ?@?"; // NOI18N
  121. final LinkedBlockingQueue<Runnable> commandQueue = new LinkedBlockingQueue<Runnable>();
  122. final ConnectionProcessor connProcessor = new ConnectionProcessor(commandQueue);
  123. final CopyOnWriteArrayList<ChangeListener> changeListeners = new CopyOnWriteArrayList<ChangeListener>();
  124. private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
  125. // Cache this in cases where it is not being saved to disk
  126. // Synchronized on the instance (this)
  127. private volatile String adminPassword;
  128. // Guarded by this
  129. private ServerState runstate = ServerState.DISCONNECTED;
  130. // This is set if checkRunning encounters an error which shows the configuration is broken
  131. // (e.g. bad host or number format error. Guarded by this.
  132. private volatile String configError = null;
  133. // Cache list of databases, refresh only if connection is changed
  134. // or an explicit refresh is requested
  135. // Synchronized on the instance (this)
  136. private volatile HashMap<String, Database> databases = new HashMap<String, Database>();
  137. public static DatabaseServer getDefault() {
  138. synchronized(lock) {
  139. if (DEFAULT != null) {
  140. return DEFAULT;
  141. }
  142. }
  143. MySQLDatabaseServer server = new MySQLDatabaseServer();
  144. synchronized(lock) {
  145. if ( DEFAULT == null ) {
  146. DEFAULT = server;
  147. }
  148. }
  149. return DEFAULT;
  150. }
  151. @SuppressWarnings("LeakingThisInConstructor")
  152. private MySQLDatabaseServer() {
  153. RequestProcessor.getDefault().post(connProcessor);
  154. MySQLOptions.getDefault().addPropertyChangeListener(this);
  155. // Setup property change listeners
  156. addPropertyChangeListener(ConnectManager.getDefault().getReconnectListener());
  157. addPropertyChangeListener(StartManager.getDefault().getStartListener());
  158. addPropertyChangeListener(StopManager.getDefault().getStopListener());
  159. RequestProcessor.getDefault().post(new Runnable() {
  160. @Override
  161. public void run() {
  162. checkRunning();
  163. }
  164. });
  165. updateDisplayInformation();
  166. }
  167. @Override
  168. public String getHost() {
  169. return Utils.isEmpty(OPTIONS.getHost()) ?
  170. MySQLOptions.getDefaultHost() : OPTIONS.getHost();
  171. }
  172. @Override
  173. public void setHost(String host) {
  174. OPTIONS.setHost(host);
  175. updateDisplayInformation();
  176. notifyChange();
  177. }
  178. @Override
  179. public String getPort() {
  180. String port = OPTIONS.getPort();
  181. if (Utils.isEmpty(port)) {
  182. return MySQLOptions.getDefaultPort();
  183. } else {
  184. return port;
  185. }
  186. }
  187. @Override
  188. public void setPort(String port) {
  189. OPTIONS.setPort(port);
  190. updateDisplayInformation();
  191. notifyChange();
  192. }
  193. @Override
  194. public String getUser() {
  195. String user = OPTIONS.getAdminUser();
  196. if (Utils.isEmpty(user)) {
  197. return MySQLOptions.getDefaultAdminUser();
  198. } else {
  199. return user;
  200. }
  201. }
  202. @Override
  203. public void setUser(String adminUser) {
  204. OPTIONS.setAdminUser(adminUser);
  205. updateDisplayInformation();
  206. notifyChange();
  207. }
  208. @Override
  209. public synchronized String getPassword() {
  210. if ( adminPassword != null ) {
  211. return adminPassword;
  212. } else{
  213. return OPTIONS.getAdminPassword();
  214. }
  215. }
  216. @Override
  217. public synchronized void setPassword(String adminPassword) {
  218. this.adminPassword = adminPassword == null ? "" : adminPassword;
  219. if ( isSavePassword() ) {
  220. OPTIONS.setAdminPassword(adminPassword);
  221. }
  222. }
  223. @Override
  224. public boolean isSavePassword() {
  225. return OPTIONS.isSavePassword();
  226. }
  227. @Override
  228. public void setSavePassword(boolean savePassword) {
  229. OPTIONS.setSavePassword(savePassword);
  230. // Save the password in case it was already set...
  231. OPTIONS.setAdminPassword(getPassword());
  232. }
  233. @Override
  234. public String getAdminPath() {
  235. return OPTIONS.getAdminPath();
  236. }
  237. @Override
  238. public void setAdminPath(String path) {
  239. OPTIONS.setAdminPath(path);
  240. }
  241. @Override
  242. public String getStartPath() {
  243. return OPTIONS.getStartPath();
  244. }
  245. @Override
  246. public void setStartPath(String path) {
  247. OPTIONS.setStartPath(path);
  248. }
  249. @Override
  250. public String getStopPath() {
  251. return OPTIONS.getStopPath();
  252. }
  253. @Override
  254. public void setStopPath(String path) {
  255. OPTIONS.setStopPath(path);
  256. }
  257. @Override
  258. public String getStopArgs() {
  259. return OPTIONS.getStopArgs();
  260. }
  261. @Override
  262. public void setStopArgs(String args) {
  263. OPTIONS.setStopArgs(args);
  264. }
  265. @Override
  266. public String getStartArgs() {
  267. return OPTIONS.getStartArgs();
  268. }
  269. @Override
  270. public void setStartArgs(String args) {
  271. OPTIONS.setStartArgs(args);
  272. }
  273. @Override
  274. public String getAdminArgs() {
  275. return OPTIONS.getAdminArgs();
  276. }
  277. @Override
  278. public void setAdminArgs(String args) {
  279. OPTIONS.setAdminArgs(args);
  280. }
  281. @Override
  282. public synchronized boolean isConnected() {
  283. return runstate == ServerState.CONNECTED;
  284. }
  285. @Override
  286. public String getDisplayName() {
  287. return displayName;
  288. }
  289. private void setDisplayName(String displayName) {
  290. this.displayName = displayName;
  291. }
  292. private void updateDisplayInformation() {
  293. String stateLabel = runstate.toString();
  294. String hostPort = getHostPort();
  295. String user = getUser();
  296. synchronized(this) {
  297. setDisplayName(Utils.getMessage("LBL_ServerDisplayName", hostPort, user, Utils.getMessage(stateLabel)));
  298. if (runstate != ServerState.CONFIGERR) {
  299. icon = ICON;
  300. setShortDescription(Utils.getMessage("LBL_ServerShortDescription", hostPort, user, Utils.getMessage(stateLabel)));
  301. } else {
  302. assert(configError != null);
  303. icon = ImageUtilities.mergeImages(ICON, ERROR_BADGE, 6, 6);
  304. setShortDescription(Utils.getMessage("LBL_ServerShortDescriptionError", configError));
  305. }
  306. }
  307. closeOutput();
  308. }
  309. @Override
  310. public String getShortDescription() {
  311. return shortDescription;
  312. }
  313. private void setShortDescription(String shortDescription) {
  314. this.shortDescription = shortDescription;
  315. }
  316. private String getHostPort() {
  317. String port = getPort();
  318. if ( Utils.isEmpty(port)) {
  319. port = "";
  320. } else {
  321. port = ":" + port;
  322. }
  323. return getHost() + port;
  324. }
  325. @Override
  326. public String getURL() {
  327. return DatabaseUtils.getURL(getHost(), getPort());
  328. }
  329. @Override
  330. public String getURL(String databaseName) {
  331. return DatabaseUtils.getURL(getHost(), getPort(), databaseName);
  332. }
  333. private void notifyChange() {
  334. ChangeEvent evt = new ChangeEvent(this);
  335. for ( ChangeListener listener : changeListeners ) {
  336. listener.stateChanged(evt);
  337. }
  338. }
  339. private void reportConnectionInvalid(DatabaseException dbe) {
  340. disconnect();
  341. LOGGER.log(Level.INFO, null, dbe);
  342. Utils.displayErrorMessage(dbe.getMessage());
  343. }
  344. @Override
  345. public void refreshDatabaseList() {
  346. if ( isConnected() ) {
  347. final DatabaseServer server = this;
  348. new DatabaseCommand() {
  349. @Override
  350. public void execute() throws Exception {
  351. try {
  352. HashMap<String,Database> dblist = new HashMap<String,Database>();
  353. if (! isConnected()) {
  354. setDatabases(dblist);
  355. return;
  356. }
  357. try {
  358. connProcessor.validateConnection();
  359. } catch (DatabaseException dbe) {
  360. reportConnectionInvalid(dbe);
  361. setDatabases(dblist);
  362. return;
  363. }
  364. Connection conn = connProcessor.getConnection();
  365. if (conn == null) {
  366. setDatabases(dblist);
  367. return;
  368. }
  369. PreparedStatement ps = conn.prepareStatement(GET_DATABASES_SQL);
  370. ResultSet rs = ps.executeQuery();
  371. while (rs.next()) {
  372. String dbname = rs.getString(1);
  373. dblist.put(dbname, new Database(server, dbname));
  374. }
  375. rs.close();
  376. ps.close();
  377. setDatabases(dblist);
  378. } finally {
  379. notifyChange();
  380. }
  381. }
  382. }.postCommand("refreshDatabaseList"); // NOI18N
  383. } else {
  384. setDatabases(new HashMap<String,Database>());
  385. notifyChange();
  386. }
  387. }
  388. private synchronized void setDatabases(HashMap<String,Database> list) {
  389. databases = list;
  390. }
  391. @Override
  392. public synchronized Collection<Database> getDatabases()
  393. throws DatabaseException {
  394. return databases.values();
  395. }
  396. private void checkNotOnDispatchThread() {
  397. if (SwingUtilities.isEventDispatchThread()) {
  398. throw new IllegalStateException("Can not call this method on the event dispatch thread");
  399. }
  400. }
  401. @Override
  402. public void disconnectSync() {
  403. disconnect(false);
  404. }
  405. @Override
  406. public void disconnect() {
  407. disconnect(true);
  408. }
  409. private void disconnect(boolean async) {
  410. ArrayBlockingQueue<Runnable> queue = null;
  411. if ( ! async ) {
  412. checkNotOnDispatchThread();
  413. queue = new ArrayBlockingQueue<Runnable>(1);
  414. }
  415. DatabaseCommand cmd = new DatabaseCommand(queue) {
  416. @Override
  417. public void execute() throws Exception {
  418. Connection conn = connProcessor.getConnection();
  419. if (conn != null) {
  420. try {
  421. conn.close();
  422. } catch (SQLException e) {
  423. // Not important, since we want to disconnect anyway.
  424. LOGGER.log(Level.FINE, null, e);
  425. }
  426. }
  427. connProcessor.setConnection(null);
  428. setState(ServerState.DISCONNECTED);
  429. updateDisplayInformation();
  430. refreshDatabaseList();
  431. }
  432. };
  433. cmd.postCommand("disconnect"); // NOI8N
  434. if (!async) {
  435. // Sync up
  436. try {
  437. cmd.syncUp();
  438. if (cmd.getException() != null) {
  439. Throwable e = cmd.getException();
  440. if (e instanceof DatabaseException) {
  441. throw new RuntimeException(e);
  442. } else {
  443. throw Utils.launderThrowable(e);
  444. }
  445. }
  446. } catch (InterruptedException ie) {
  447. throw new RuntimeException(ie);
  448. }
  449. }
  450. }
  451. @Override
  452. public void reconnect() throws DatabaseException, TimeoutException {
  453. reconnect(10000);
  454. }
  455. @Override
  456. public void reconnect(long timeToWait) throws DatabaseException, TimeoutException {
  457. ArrayBlockingQueue<Runnable> queue = null;
  458. checkNotOnDispatchThread();
  459. queue = new ArrayBlockingQueue<Runnable>(1);
  460. DatabaseCommand cmd = new DatabaseCommand(queue) {
  461. @Override
  462. public void execute() throws Exception {
  463. disconnectSync();
  464. checkConfiguration();
  465. ProgressHandle progress = ProgressHandleFactory.createHandle(
  466. Utils.getMessage("MSG_ConnectingToServer"));
  467. try {
  468. progress.start();
  469. progress.switchToIndeterminate();
  470. Connection conn = DatabaseUtils.connect(getURL(), getUser(), getPassword());
  471. if (conn == null) {
  472. throw new DatabaseException(NbBundle.getMessage(MySQLDatabaseServer.class, "MSG_UnableToConnect", getURL(), getUser())); // NOI8N
  473. }
  474. connProcessor.setConnection(conn);
  475. setState(ServerState.CONNECTED);
  476. } catch (DatabaseException dbe) {
  477. disconnect();
  478. throw dbe;
  479. } catch (TimeoutException te) {
  480. disconnect();
  481. throw te;
  482. }
  483. finally {
  484. refreshDatabaseList();
  485. progress.finish();
  486. }
  487. }
  488. };
  489. cmd.postCommand("reconnect"); // NOI18N
  490. // Sync up
  491. try {
  492. cmd.syncUp();
  493. if (cmd.getException() != null) {
  494. if (cmd.getException() instanceof DatabaseException) {
  495. throw new DatabaseException(cmd.getException());
  496. } else if (cmd.getException() instanceof TimeoutException) {
  497. TimeoutException newte = new TimeoutException(cmd.getException().getMessage());
  498. newte.initCause(cmd.getException());
  499. throw newte;
  500. } else {
  501. throw Utils.launderThrowable(cmd.getException());
  502. }
  503. }
  504. } catch (InterruptedException ie) {
  505. LOGGER.log(Level.INFO, null, ie);
  506. Thread.currentThread().interrupt();
  507. }
  508. }
  509. @Override
  510. public void checkConfiguration() throws DatabaseException {
  511. // Make sure the host name is a known host name
  512. try {
  513. InetAddress.getAllByName(getHost());
  514. } catch (UnknownHostException ex) {
  515. synchronized(this) {
  516. configError = NbBundle.getMessage(MySQLDatabaseServer.class, "MSG_UnknownHost", getHost());
  517. setState(ServerState.CONFIGERR);
  518. }
  519. LOGGER.log(Level.INFO, configError, ex);
  520. throw new DatabaseException(configError, ex);
  521. }
  522. try {
  523. String port = getPort();
  524. if (port == null) {
  525. throw new NumberFormatException();
  526. }
  527. Integer.valueOf(port);
  528. } catch (NumberFormatException nfe) {
  529. synchronized(this) {
  530. configError = NbBundle.getMessage(MySQLDatabaseServer.class, "MSG_InvalidPortNumber", getPort());
  531. setState(ServerState.CONFIGERR);
  532. }
  533. LOGGER.log(Level.INFO, configError, nfe);
  534. throw new DatabaseException(configError, nfe);
  535. }
  536. }
  537. @Override
  538. public void validateConnection() throws DatabaseException {
  539. ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(1);
  540. DatabaseCommand cmd = new DatabaseCommand(queue, true) {
  541. @Override
  542. public void execute() throws Exception {
  543. // Just run the preprocessing for execute() which checks the
  544. // status of the server and the connection.
  545. }
  546. };
  547. cmd.postCommand("validateConnection"); // NOI18N
  548. try {
  549. cmd.syncUp();
  550. Throwable e = cmd.getException();
  551. if (e != null) {
  552. if (e instanceof DatabaseException) {
  553. throw (DatabaseException)e;
  554. } else {
  555. throw Utils.launderThrowable(e);
  556. }
  557. }
  558. } catch (InterruptedException e) {
  559. disconnect();
  560. throw new DatabaseException(e);
  561. }
  562. }
  563. @Override
  564. public boolean databaseExists(String dbname) throws DatabaseException {
  565. return databases.containsKey(dbname);
  566. }
  567. @Override
  568. public void createDatabase(final String dbname) {
  569. new DatabaseCommand(true) {
  570. @Override
  571. public void execute() throws Exception {
  572. try {
  573. Connection conn = connProcessor.getConnection();
  574. Quoter quoter = connProcessor.getQuoter();
  575. String quotedName = quoter.quoteIfNeeded(dbname);
  576. PreparedStatement stmt = conn.prepareStatement(CREATE_DATABASE_SQL + quotedName);
  577. stmt.executeUpdate();
  578. stmt.close();
  579. } finally {
  580. refreshDatabaseList();
  581. }
  582. }
  583. }.postCommand("createDatabase"); // NOI18N
  584. }
  585. @Override
  586. public void dropDatabase(final String dbname, final boolean deleteConnections) {
  587. new DatabaseCommand(true) {
  588. @Override
  589. public void execute() throws Exception {
  590. try {
  591. Connection conn = connProcessor.getConnection();
  592. Quoter quoter = connProcessor.getQuoter();
  593. String quotedName = quoter.quoteIfNeeded(dbname);
  594. PreparedStatement stmt = conn.prepareStatement(DROP_DATABASE_SQL + quotedName);
  595. stmt.executeUpdate();
  596. stmt.close();
  597. if (deleteConnections) {
  598. String hostname = getHost();
  599. String ipaddr = Utils.getHostIpAddress(hostname);
  600. DatabaseConnection[] dbconns = ConnectionManager.getDefault().getConnections();
  601. for (DatabaseConnection dbconn : dbconns) {
  602. if (dbconn.getDriverClass().equals(MySQLOptions.getDriverClass()) &&
  603. dbconn.getDatabaseURL().contains("/" + dbname) &&
  604. (dbconn.getDatabaseURL().contains(getHost()) ||
  605. dbconn.getDatabaseURL().contains(ipaddr)) &&
  606. dbconn.getDatabaseURL().contains(getPort())) {
  607. ConnectionManager.getDefault().removeConnection(dbconn);
  608. }
  609. }
  610. }
  611. } finally {
  612. refreshDatabaseList();
  613. }
  614. }
  615. }.postCommand("dropDatabase"); // NOI18N
  616. }
  617. @Override
  618. public void dropDatabase(final String dbname) {
  619. dropDatabase(dbname, true);
  620. }
  621. /**
  622. * Get the list of users defined for this server
  623. *
  624. * @return the list of users
  625. *
  626. * @throws org.netbeans.api.db.explorer.DatabaseException
  627. * if some problem occurred
  628. */
  629. @Override
  630. public List<DatabaseUser> getUsers() throws DatabaseException {
  631. final ArrayList<DatabaseUser> users = new ArrayList<DatabaseUser>();
  632. if ( ! isConnected() ) {
  633. return users;
  634. }
  635. ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(1);
  636. DatabaseCommand cmd = new DatabaseCommand(queue, true) {
  637. @Override
  638. public void execute() throws Exception {
  639. PreparedStatement stmt = connProcessor.getConnection().
  640. prepareStatement(GET_USERS_SQL);
  641. ResultSet rs = stmt.executeQuery();
  642. while ( rs.next() ) {
  643. String user = rs.getString(1).trim();
  644. String host = rs.getString(2).trim();
  645. users.add(new DatabaseUser(user, host));
  646. }
  647. rs.close();
  648. stmt.close();
  649. }
  650. };
  651. cmd.postCommand("getUsers"); // NOI18N
  652. // Synch up
  653. try {
  654. cmd.syncUp();
  655. if (cmd.getException() != null) {
  656. Throwable e = cmd.getException();
  657. if (e instanceof DatabaseException) {
  658. throw (DatabaseException)e;
  659. } else if (e.getClass().getName().contains("MySQLSyntaxErrorException")) { // NOI18N
  660. throw new DatabaseException(e);
  661. } else {
  662. throw Utils.launderThrowable(e);
  663. }
  664. }
  665. } catch ( InterruptedException e ) {
  666. throw new DatabaseException(e);
  667. }
  668. return users;
  669. }
  670. @Override
  671. public void grantFullDatabaseRights(final String dbname, final DatabaseUser grantUser) {
  672. new DatabaseCommand(true) {
  673. @Override
  674. public void execute() throws Exception {
  675. String quotedName = connProcessor.getQuoter().quoteIfNeeded(dbname);
  676. PreparedStatement ps = connProcessor.getConnection().
  677. prepareStatement(GRANT_ALL_SQL_1 + quotedName + GRANT_ALL_SQL_2);
  678. ps.setString(1, grantUser.getUser());
  679. ps.setString(2, grantUser.getHost());
  680. ps.executeUpdate();
  681. ps.close();
  682. }
  683. }.postCommand("grantFullDatabaseRights"); // NOI8N
  684. }
  685. /**
  686. * Run the start command. Display stdout and stderr to an output
  687. * window. Wait the configured wait time, attempt to connect, and
  688. * then return.
  689. *
  690. * @return true if the server is definitely started, false otherwise (the server is
  691. * not started or the status is unknown).
  692. *
  693. * @throws org.netbeans.api.db.explorer.DatabaseException
  694. *
  695. * @see #getStartWaitTime()
  696. */
  697. @Override
  698. public void start() throws DatabaseException {
  699. if (!Utils.isValidExecutable(getStartPath(), false)) {
  700. throw new DatabaseException(Utils.getMessage("MSG_InvalidStartCommand"));
  701. }
  702. new DatabaseCommand() {
  703. @Override
  704. public void execute() throws Exception {
  705. ServerState state = checkRunning(1000);
  706. if (state == ServerState.CONNECTED) {
  707. return;
  708. }
  709. try {
  710. runProcess(getStartPath(), getStartArgs());
  711. } finally {
  712. updateDisplayInformation();
  713. notifyChange();
  714. }
  715. }
  716. }.postCommand("start"); // NOI18N
  717. }
  718. @Override
  719. public void stop() throws DatabaseException {
  720. if ( !Utils.isValidExecutable(getStopPath(), false)) {
  721. throw new DatabaseException(Utils.getMessage("MSG_InvalidStopCommand"));
  722. }
  723. new StopDatabaseCommand().postCommand("stop"); // NOI18N
  724. }
  725. /**
  726. * Launch the admin tool. If the specified admin path is a URL,
  727. * a browser is launched with the URL. If the specified admin path
  728. * is a file, the file path is executed.
  729. *
  730. * @return a process object for the executed command if the admin
  731. * path was a file. Returns null if the browser was launched.
  732. *
  733. * @throws org.netbeans.api.db.explorer.DatabaseException
  734. */
  735. @Override
  736. public void startAdmin() throws DatabaseException {
  737. String adminCommand = getAdminPath();
  738. if ( adminCommand == null || adminCommand.length() == 0) {
  739. throw new DatabaseException(NbBundle.getMessage(
  740. DatabaseServer.class,
  741. "MSG_AdminCommandNotSet"));
  742. }
  743. if ( Utils.isValidURL(adminCommand, false)) {
  744. launchBrowser(adminCommand);
  745. } else if ( Utils.isValidExecutable(adminCommand, false)) {
  746. runProcess(adminCommand, getAdminArgs());
  747. closeOutput();
  748. } else {
  749. throw new DatabaseException(NbBundle.getMessage(
  750. DatabaseServer.class,
  751. "MSG_InvalidAdminCommand", adminCommand));
  752. }
  753. }
  754. private Process runProcess(String command, String args) throws DatabaseException {
  755. if ( Utilities.isMac() && command.endsWith(".app") ) { // NOI18N
  756. // The command is actually the first argument, with /usr/bin/open
  757. // as the actual command. Put the .app file path in quotes to
  758. // deal with spaces in the path.
  759. args = "\"" + command + "\" " + args; // NOI18N
  760. command = "/usr/bin/open"; // NOI18N
  761. }
  762. try {
  763. NbProcessDescriptor desc = new NbProcessDescriptor(command, args);
  764. Process proc = desc.exec();
  765. new ExecSupport().displayProcessOutputs(proc);
  766. return proc;
  767. } catch ( Exception e ) {
  768. throw new DatabaseException(e);
  769. }
  770. }
  771. private void launchBrowser(String adminCommand) throws DatabaseException {
  772. try {
  773. HtmlBrowser.URLDisplayer.getDefault().showURL(new URL(adminCommand));
  774. } catch ( Exception e ) {
  775. throw new DatabaseException(e);
  776. }
  777. }
  778. @Override
  779. public void addChangeListener(ChangeListener listener) {
  780. changeListeners.add(listener);
  781. }
  782. @Override
  783. public void removeChangeListener(ChangeListener listener) {
  784. changeListeners.remove(listener);
  785. }
  786. @Override
  787. public void addPropertyChangeListener(PropertyChangeListener listener) {
  788. pcs.addPropertyChangeListener(listener);
  789. }
  790. @Override
  791. public void removePropertyChangeListener(PropertyChangeListener listener) {
  792. pcs.removePropertyChangeListener(listener);
  793. }
  794. @Override
  795. public synchronized ServerState getState() {
  796. return runstate;
  797. }
  798. private void setState(ServerState runstate) {
  799. synchronized(this) {
  800. this.runstate = runstate;
  801. if (runstate != ServerState.CONFIGERR) {
  802. configError = null;
  803. }
  804. updateDisplayInformation();
  805. }
  806. notifyChange();
  807. }
  808. public ServerState checkRunning() {
  809. return checkRunning(5000);
  810. }
  811. public ServerState checkRunning(long timeToWait) {
  812. try {
  813. reconnect(timeToWait);
  814. } catch (DatabaseException dbe) {
  815. LOGGER.log(Level.FINE, null, dbe);
  816. } catch (TimeoutException dbe) {
  817. LOGGER.log(Level.INFO, null, dbe);
  818. }
  819. return runstate;
  820. }
  821. @Override
  822. public void propertyChange(PropertyChangeEvent evt) {
  823. pcs.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
  824. }
  825. @Override
  826. public Image getIcon() {
  827. return icon;
  828. }
  829. @Override
  830. public synchronized boolean hasConfigurationError() {
  831. return runstate == ServerState.CONFIGERR;
  832. }
  833. public static void writeOutput(String msg) {
  834. synchronized (MySQLDatabaseServer.class) {
  835. getOutput().getOut().println(msg);
  836. }
  837. }
  838. public static InputOutput getOutput() {
  839. synchronized (MySQLDatabaseServer.class) {
  840. if (OUTPUT == null) {
  841. OUTPUT = IOProvider.getDefault().getIO(Utils.getMessage("LBL_MySQLOutputTab"), false); // NOI18N
  842. }
  843. OUTPUT.select();
  844. return OUTPUT;
  845. }
  846. }
  847. private static void closeOutput() {
  848. synchronized (MySQLDatabaseServer.class) {
  849. if (OUTPUT != null) {
  850. OUTPUT.getOut().close();
  851. }
  852. }
  853. }
  854. private abstract class DatabaseCommand implements Runnable {
  855. private Throwable throwable;
  856. private final BlockingQueue<Runnable> outqueue;
  857. private boolean checkConnection = false;
  858. private String callingMethod = "<unknown>"; // NOI18N
  859. public DatabaseCommand(BlockingQueue<Runnable> outqueue) {
  860. this(outqueue, false);
  861. }
  862. public DatabaseCommand(BlockingQueue<Runnable> outqueue, boolean checkConnection) {
  863. this.outqueue = outqueue;
  864. this.checkConnection = checkConnection;
  865. }
  866. public DatabaseCommand(boolean checkConnection) {
  867. this(null, checkConnection);
  868. }
  869. public DatabaseCommand() {
  870. this(null, false);
  871. }
  872. public void postCommand(String callingMethod) {
  873. this.callingMethod = callingMethod;
  874. if (connProcessor.isConnProcessorThread()) {
  875. run();
  876. } else {
  877. commandQueue.offer(this);
  878. }
  879. }
  880. public void syncUp() throws InterruptedException {
  881. if (connProcessor.isConnProcessorThread()) {
  882. return;
  883. } else {
  884. assert(outqueue != null);
  885. outqueue.take();
  886. }
  887. }
  888. @Override
  889. public void run() {
  890. try {
  891. if (checkConnection) {
  892. try {
  893. connProcessor.validateConnection();
  894. } catch (DatabaseException dbe) {
  895. try {
  896. // See if we can quickly reconnect...
  897. reconnect();
  898. } catch (DatabaseException dbe2) {
  899. LOGGER.log(Level.INFO, null, dbe2);
  900. disconnect();
  901. throw dbe;
  902. }
  903. }
  904. }
  905. this.execute();
  906. } catch ( DatabaseException e ) {
  907. if ( outqueue != null ) {
  908. this.throwable = e;
  909. } else {
  910. // Since this is asynchronous, we are responsible for reporting the exception to the user.
  911. LOGGER.log(Level.INFO, NbBundle.getMessage(MySQLDatabaseServer.class, "MSG_DatabaseCommandFailed", callingMethod), e);
  912. Utils.displayErrorMessage(e.getMessage());
  913. }
  914. } catch (Exception e) {
  915. if (outqueue != null) {
  916. this.throwable = e;
  917. } else {
  918. this.throwable = e;
  919. // Since this is asynchronous, we are responsible for reporting the exception to the user.
  920. Utils.displayErrorMessage(
  921. NbBundle.getMessage(MySQLDatabaseServer.class, "MSG_DatabaseCommandFailed", callingMethod, e.getMessage())); // NOI18N
  922. }
  923. } finally {
  924. if (outqueue != null) {
  925. outqueue.offer(this);
  926. }
  927. }
  928. }
  929. public abstract void execute() throws Exception;
  930. public Throwable getException() {
  931. return throwable;
  932. }
  933. }
  934. private class StopDatabaseCommand extends DatabaseCommand implements Cancellable {
  935. private Process proc = null;
  936. @Override
  937. public void execute() throws Exception {
  938. ProgressHandle handle = ProgressHandleFactory.createHandle(Utils.getMessage("LBL_StoppingMySQLServer"), this);
  939. try {
  940. handle.start();
  941. handle.switchToIndeterminate();
  942. proc = runProcess(getStopPath(), getStopArgs());
  943. // wait until server is shut down
  944. proc.waitFor();
  945. } finally {
  946. if (proc != null) {
  947. proc.destroy();
  948. closeOutput();
  949. }
  950. handle.finish();
  951. }
  952. }
  953. @Override
  954. public boolean cancel() {
  955. proc.destroy();
  956. closeOutput();
  957. return true;
  958. }
  959. }
  960. }