001 package org.apache.fulcrum.hsqldb; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import org.apache.avalon.framework.activity.Disposable; 023 import org.apache.avalon.framework.activity.Initializable; 024 import org.apache.avalon.framework.activity.Startable; 025 import org.apache.avalon.framework.configuration.Configurable; 026 import org.apache.avalon.framework.configuration.Configuration; 027 import org.apache.avalon.framework.configuration.ConfigurationException; 028 import org.apache.avalon.framework.logger.AbstractLogEnabled; 029 import org.hsqldb.Server; 030 import org.hsqldb.ServerConstants; 031 import org.hsqldb.persist.HsqlProperties; 032 033 /** 034 * The original implementation was taken from 035 * http://scarab.tigris.org/source/browse/scarab/src/java/org/tigris/scarab/services/hsql/ 036 * and tweaked a little bit. 037 * 038 * <p> 039 * The component is configured from the componentConfig.xml file by specifying 040 * attributes on the service element 041 * </p> 042 * <p> 043 * 044 * <dl> 045 * <dt>database</dt> 046 * <dd>The directory path where the database files will be stored</dd> 047 * <dt>dbname</dt> 048 * <dd>The alias path used to refer to the database from the JDBC url.</dd> 049 * <dt>trace</dt> 050 * <dd>(true/false) a flag enabling tracing in the hsql server.</dd> 051 * <dt>silent</dt> 052 * <dd>(true/false) a flag to control the logging output of the hsql server.</dd> 053 * <dt>start</dt> 054 * <dd>(true/false) when true the database is started at configuration time, and does 055 * not need to be started under application control.</dd> 056 * <dt>port</dt> 057 * <dd>The listening port of the hsql server.</dd> 058 * </dl> 059 * 060 * Example: 061 * ... 062 * <HSQLService database="./target" dbname="test" trace="true" silent="false" start="true" port="9001"/> 063 * ... 064 * 065 * @author <a href="mailto:pti@elex.be">Peter Tillemans</a> 066 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a> 067 */ 068 public class HSQLServiceImpl 069 extends AbstractLogEnabled 070 implements HSQLService, Configurable, Initializable, Startable, Disposable 071 { 072 /** the HSQLDB server instance */ 073 private Server server; 074 075 /** the configuration properties */ 076 private HsqlProperties serverProperties; 077 078 ///////////////////////////////////////////////////////////////////////// 079 // Avalon Service Lifecycle Implementation 080 ///////////////////////////////////////////////////////////////////////// 081 082 /** 083 * Constructor 084 */ 085 public HSQLServiceImpl() 086 { 087 // nothing to do 088 } 089 090 /** 091 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) 092 */ 093 public void configure(Configuration cfg) throws ConfigurationException 094 { 095 String[] names = cfg.getAttributeNames(); 096 097 for (int i = 0; i < names.length; i++) 098 { 099 getLogger().debug(names[i] + " --> " + cfg.getAttribute(names[i])); 100 } 101 102 this.serverProperties = new HsqlProperties(); 103 this.serverProperties.setProperty("server.database.0", cfg.getAttribute("database")); 104 this.serverProperties.setProperty("server.dbname.0", cfg.getAttribute("dbname")); 105 this.serverProperties.setProperty("server.trace", cfg.getAttributeAsBoolean("trace", false)); 106 this.serverProperties.setProperty("server.silent", cfg.getAttributeAsBoolean("silent", true)); 107 this.serverProperties.setProperty("server.port", cfg.getAttribute("port")); 108 this.serverProperties.setProperty("server.tls", cfg.getAttribute("tls","false")); 109 } 110 111 /** 112 * @see org.apache.avalon.framework.activity.Initializable#initialize() 113 */ 114 public void initialize() throws Exception 115 { 116 this.server = new Server(); 117 this.server.setProperties( this.serverProperties ); 118 } 119 120 /** 121 * Starts the HSQLDB server. The implementation polls to ensure 122 * that the HSQLDB server is fully initialized otherwise we get 123 * spurious connection exceptions. If the HSQLDB server is not 124 * upand running within 10 seconds we throw an exception. 125 * 126 * @see org.apache.avalon.framework.activity.Startable#start() 127 */ 128 public void start() throws Exception 129 { 130 // The method start() waits for current state to change from 131 // SERVER_STATE_OPENING. In order to discover the success or failure 132 // of this operation, server state must be polled or a subclass of Server 133 // must be used that overrides the setState method to provide state 134 // change notification. 135 136 server.start(); 137 138 // poll for 10 seconds until HSQLDB is up and running 139 140 this.pollForState( ServerConstants.SERVER_STATE_ONLINE, 100 ); 141 } 142 143 /** 144 * Stop the HSQLDB server. The implementation polls to ensure 145 * that the HSQLDB server has terminated otherwise someone 146 * could call System.exit() and break the database. 147 * 148 * @see org.apache.avalon.framework.activity.Startable#stop() 149 */ 150 public void stop() throws Exception 151 { 152 this.server.stop(); 153 154 // poll for 10 seconds until HSQLDB is down 155 156 this.pollForState( ServerConstants.SERVER_STATE_SHUTDOWN, 100 ); 157 } 158 159 /** 160 * @see org.apache.avalon.framework.activity.Disposable#dispose() 161 */ 162 public void dispose() 163 { 164 this.server = null; 165 this.serverProperties = null; 166 } 167 168 ///////////////////////////////////////////////////////////////////////// 169 // Service Interface Implementation 170 ///////////////////////////////////////////////////////////////////////// 171 172 public boolean isOnline() { 173 return server.getState() == ServerConstants.SERVER_STATE_ONLINE; 174 } 175 176 177 ///////////////////////////////////////////////////////////////////////// 178 // Service Implementation 179 ///////////////////////////////////////////////////////////////////////// 180 181 /** 182 * Poll the HSQLDB server for a state change. 183 * 184 * @param desiredState the state we are waiting for 185 * @param lim the number of 100ms iteration to wait for 186 * @throws Exception something went wrong 187 */ 188 private void pollForState( int desiredState, int lim ) 189 throws Exception 190 { 191 int currentState; 192 boolean isSuccessful = false; 193 194 this.getLogger().debug( "Polling for state : " + desiredState ); 195 196 for( int i=0; i<lim; i++ ) 197 { 198 currentState = this.server.getState(); 199 200 if( desiredState == currentState ) 201 { 202 isSuccessful = true; 203 break; 204 } 205 206 Thread.sleep(100); 207 } 208 209 if( !isSuccessful ) 210 { 211 212 Throwable serverError = this.server.getServerError(); 213 String msg = "Unable to change the HSQLDB server to state : " + desiredState; 214 215 if( serverError != null ) 216 { 217 this.getLogger().error( msg, serverError ); 218 219 if( serverError instanceof Exception ) 220 { 221 throw (Exception) serverError; 222 } 223 else 224 { 225 throw new RuntimeException( serverError.getMessage() ); 226 } 227 } 228 else 229 { 230 this.getLogger().error(msg); 231 throw new RuntimeException( msg ); 232 } 233 } 234 } 235 }