/** * EasyBeans * Copyright (C) 2006 Bull S.A.S. * Contact: easybeans@ow2.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * -------------------------------------------------------------------------- * $Id: HSQLDBComponent.java 5752 2011-03-01 12:48:22Z benoitf $ * -------------------------------------------------------------------------- */ package org.ow2.easybeans.component.hsqldb; import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Iterator; import java.util.List; import org.hsqldb.DatabaseManager; import org.hsqldb.Server; import org.hsqldb.ServerConstants; import org.ow2.easybeans.component.api.EZBComponentException; import org.ow2.easybeans.component.itf.EmbeddedDBComponent; import org.ow2.util.log.Log; import org.ow2.util.log.LogFactory; /** * Allows to start an embedded HSQLDB server. * @author Florent Benoit */ public class HSQLDBComponent implements EmbeddedDBComponent { /** * Logger. */ private static Log logger = LogFactory.getLog(HSQLDBComponent.class); /** * List of users. */ private List<User> users = null; /** * Name of database. */ private String databaseName = null; /** * Default port number. */ private static final String DEFAULT_PORT = "9001"; /** * Default port number. */ private static final String DEFAULT_HOST = "localhost"; /** * Sleep value. */ private static final int SLEEP_VALUE = 100; /** * Max retry number. */ private static final int MAX_RETRY_NB = 50; /** * port number used. */ private String portNumber = null; /** * Hostname to use. */ private String hostname = null; /** * HsqlDB server. */ private Server server = null; /** * String path. */ private String path = null; /** * Default constructor.<br> * Use default port number + hostname. */ public HSQLDBComponent() { this.portNumber = DEFAULT_PORT; this.hostname = DEFAULT_HOST; } /** * Init method.<br/> This method is called before the start method. * @throws EZBComponentException if the initialization has failed. */ public void init() throws EZBComponentException { this.server = new Server(); // Remove all traces if level != DEBUG if (!logger.isDebugEnabled()) { this.server.setLogWriter(null); this.server.setErrWriter(null); this.server.setSilent(true); this.server.setTrace(false); this.server.setLogWriter(null); } else { // Enable all traces : verbose mode (as user needs DEBUG) this.server.setSilent(false); this.server.setTrace(true); } // Use a specified path or go in temp directory ? String baseDir = null; if (this.path != null) { baseDir = this.path + File.separator + this.databaseName; } else { baseDir = System.getProperty("java.io.tmpdir") + File.separator + "easybeans" + File.separator + "hsqldb" + File.separator + this.databaseName; } String pString = ""; if (this.portNumber != null) { pString = ";port=" + this.portNumber; } String serverProps = "database.0=" + baseDir + ";dbname.0=" + this.databaseName + pString; logger.debug("Server properties = {0}", serverProps); this.server.putPropertiesFromString(serverProps); // Specify hostname to use this.server.setAddress(this.hostname); try { Class.forName("org.hsqldb.jdbcDriver"); } catch (ClassNotFoundException e) { throw new EZBComponentException("Cannot access to HSQL Driver 'org.hsqldb.jdbcDriver'.", e); } } /** * Start method.<br/> This method is called after the init method. * @throws EZBComponentException if the start has failed. */ @SuppressWarnings("boxing") public void start() throws EZBComponentException { logger.info("Starting ''{0}'' ''{1}'' on port ''{2}''", this.server.getProductName(), this.server.getProductVersion(), this.portNumber); this.server.start(); // Wait the start int retryNb = 0; while (this.server.getState() != ServerConstants.SERVER_STATE_ONLINE) { try { Thread.sleep(SLEEP_VALUE); } catch (InterruptedException ie) { logger.error("Cannot wait that the service is online", ie); } // Error if server state is "SHUTDOWN" during a long period // Maybe strange but 'SHUTDOWN' state seems to be an intermediate // state during startup retryNb++; if (this.server.getState() == ServerConstants.SERVER_STATE_SHUTDOWN && retryNb >= MAX_RETRY_NB) { Throwable t = this.server.getServerError(); throw new EZBComponentException("Cannot start the server. The server has not started and is shutdown.", t); } logger.debug("retry= {0}, serverState= {1}", retryNb, this.server.getState()); } String connURL = "jdbc:hsqldb:hsql://" + this.hostname + ":" + this.portNumber + "/" + this.databaseName; logger.info("{0} started with URL {1}", this.server.getProductName(), connURL); Connection conn = null; Statement st = null; try { conn = DriverManager.getConnection(connURL, "sa", ""); } catch (SQLException e) { throw new EZBComponentException("Cannot access to HSQL", e); } try { st = conn.createStatement(); } catch (SQLException e) { try { conn.close(); } catch (SQLException connEx) { logger.error("Error while closing connection object", connEx); } throw new EZBComponentException("Cannot access to HSQL", e); } // Drop users before recreating it User user = null; String userName = null; String password = null; ResultSet rs = null; for (Iterator<User> it = this.users.iterator(); it.hasNext();) { user = it.next(); try { password = user.getPassword(); userName = user.getUserName(); logger.debug("Dropping and adding user {0} with password {1}.", userName, password); try { rs = st.executeQuery("DROP USER " + userName); } catch (SQLException e) { logger.debug("User {0} doesn't exists", userName, e); } rs = st.executeQuery("Create USER " + userName + " PASSWORD " + password + " ADMIN"); rs.close(); } catch (SQLException e) { logger.error("Error while creating/adding user", e); } } try { st.close(); conn.close(); } catch (SQLException e) { logger.error("Error while closing statement object", e); } } /** * Gets the list of users. * @return the list of users. */ public List<User> getUsers() { return this.users; } /** * Set the list of users. * @param users the list of users. */ public void setUsers(final List<User> users) { this.users = users; } /** * Stop method.<br/> This method is called when component needs to be * stopped. * @throws EZBComponentException if the stop is failing. */ public void stop() throws EZBComponentException { this.server.shutdown(); DatabaseManager.getTimer().shutDown(); } /** * Sets the port number. * @param portNumber the port number to use. */ public void setPortNumber(final String portNumber) { this.portNumber = portNumber; } /** * Sets the hostname. * @param hostname the hostname to use. */ public void setHostname(final String hostname) { this.hostname = hostname; } /** * Sets the database name. * @param databaseName the name of the database. */ public void setDatabaseName(final String databaseName) { this.databaseName = databaseName; } /** * Allows to change the path of the database files. * @param path the path of the files for storing the database. */ public void setPath(final String path) { this.path = path; } }