1 /**
2 * Copyright 2005-2006 the original author or authors.
3 *
4 * Licensed under the Gnu General Pubic License, Version 2.0 (the
5 * "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/gpl-license.php
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the Gnu General Public License for more details.
14 */
15 package org.figure8.join.core.setup;
16
17 import org.figure8.join.util.LogUtil;
18 import org.figure8.join.util.ConnectionKeeperLauncher;
19
20 import org.apache.commons.logging.Log;
21
22 import javax.sql.DataSource;
23 import javax.naming.InitialContext;
24
25 import java.io.File;
26 import java.net.URI;
27 import java.net.Socket;
28 import java.net.ServerSocket;
29 import java.net.InetAddress;
30 import java.sql.Connection;
31 import java.sql.DriverManager;
32 /**
33 * Manager object responsible of Join application bootstrapping.
34 * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
35 * @version $Revision: 1.3 $
36 */
37 public class BootstrapManager{
38
39
40
41 /** Get a commons logger. */
42 protected static Log log = LogUtil.getLog(BootstrapManager.class);
43
44
45
46
47 /** Flag telling if bootstrap has succeed. */
48 private boolean bootstrapped = false;
49 /** Join application home locator. */
50 private JoinHomeLocator homeLocator = null;
51 /** Application configuration wrapper. */
52 private ApplicationConfig appConfig = null;
53 /** ActiveMQ configuration wrapper. */
54 private ActiveMQConfigurator amqConfigurator = null;
55 /** Hibernate configurator wrapper. */
56 private HibernateConfigurator hbnConfigurator = null;
57
58
59
60
61 /** Creates a new instance of BootstrapManager */
62 public BootstrapManager(){
63 }
64
65
66
67
68 /** @param locator <code>JoinHomeLocator</code> instance */
69 public void setHomeLocator(JoinHomeLocator locator){
70 this.homeLocator = locator;
71 }
72 /** @param config <code>ApplicationConfig</code> instance */
73 public void setApplicationConfig(ApplicationConfig config){
74 this.appConfig = config;
75 }
76 /** @param configurator <code>ActiveMQConfigurator</code> instance */
77 public void setMessagingConfigurator(ActiveMQConfigurator configurator){
78 this.amqConfigurator = configurator;
79 }
80 /** @param configurator <code>HibernateConfigurator</code> instance */
81 public void setHibernateConfigurator(HibernateConfigurator configurator){
82 this.hbnConfigurator = configurator;
83 }
84
85
86
87
88 /**
89 * Bootstrap the Join application ! First step is to try locating Join
90 * home path. Then load application configuration.
91 * @throws BootstrapException if Join home cannot be located or if parsing
92 * the application configuration file failed.
93 */
94 public void bootstrap() throws BootstrapException{
95
96 if (homeLocator.getHomePath() != null){
97 appConfig.setApplicationHome(homeLocator.getHomePath());
98
99 if (appConfig.configurationFileExists())
100 appConfig.load();
101 }
102 else{
103 log.error("Unable to set up application config : no join home set");
104 throw new BootstrapException("Unable to boot application : no join home set");
105 }
106
107 bootstrapped = true;
108 }
109
110
111
112
113 /**
114 * Tell if bootstrap phase has ran ok.
115 * @return boostrapped flag.
116 */
117 public boolean isBootstrapped(){
118 return bootstrapped;
119 }
120
121 /**
122 * Convenient method for knowing if application setup is complete.
123 * @return The corresponding flag on {@code ApplicationConfig}
124 */
125 public boolean isSetupComplete(){
126
127 return appConfig.isSetupComplete();
128 }
129 /**
130 * Convenient method for knowing if application setup is a custom setup.
131 * @return The corresponding flag on {@code ApplicationConfig}
132 */
133 public boolean isCustomSetup(){
134
135 return appConfig.isCustomSetup();
136 }
137 /**
138 * Convenient method for knowing if application setup is a standard setup.
139 * @return The corresponding flag on {@code ApplicationConfig}
140 */
141 public boolean isStandardSetup(){
142
143 return appConfig.isStandardSetup();
144 }
145 /**
146 * Convenient method for knowing it activeMQ setup is complete.
147 * @return true if the setup is complete, false otherwise
148 */
149 public boolean isMessagingSetup(){
150 String setup = appConfig.getProperty(ActiveMQConfigurator.AMQ_SETUP_PROPERTY);
151 if (setup != null && setup.equalsIgnoreCase("true"))
152 return true;
153 return false;
154 }
155 /**
156 * Convenient method for knowing if hibernate setup is complete.
157 * @return true if the setup is complete, false otherwise
158 */
159 public boolean isHibernateSetup(){
160 String setup = appConfig.getProperty(HibernateConfigurator.HBN_SETUP_PROPERTY);
161 if (setup != null && setup.equalsIgnoreCase("true"))
162 return true;
163 return false;
164 }
165
166 /** @return true if this side of application is synchronous */
167 public boolean isSynchronousSide(){
168
169 return appConfig.isSynchronousSide();
170 }
171 /** @return true if this side of application is asynchronous */
172 public boolean isAsynchronousSide(){
173
174 return appConfig.isAsynchronousSide();
175 }
176 /** @return true if this setup is custom with dissociation of synch/asynch services */
177 public boolean isDissociatedSetup(){
178
179 return appConfig.isDissociatedSetup();
180 }
181 /**
182 * Convenient method for knowing the Url of application other side.
183 * This url only exists if application has a dissociated setup type.
184 * @return The url of the other side of application, or null if it has no sense...
185 */
186 public String getOtherSideUrl(){
187
188 return appConfig.getProperty(ApplicationConfig.OTHER_SIDE_URL);
189 }
190
191 /**
192 * Retrieve Join home location onto filesystem.
193 * @return String representing Join home location (or null if not defined)
194 */
195 public String getJoinHome(){
196 String joinHome = appConfig.getApplicationHome();
197 if (joinHome == null)
198 log.fatal("${join.home} has not been configured. Please check your join home configuration");
199
200 return joinHome;
201 }
202
203 /**
204 * Convenient method for retrieving application configuration property.
205 * @param property The name of the property of retrieve
206 * @return The value of the corresponding application property.
207 */
208 public String getProperty(String property){
209
210 return appConfig.getProperty(property);
211 }
212 /**
213 * Convenient method for setting an application config property.
214 * @param key Unique id of the application property
215 * @param value Value associated to key
216 * @see org.figure8.join.core.setup.ApplicationConfig#setProperty(String, Object)
217 */
218 public void setProperty(String key, Object value){
219
220 appConfig.setProperty(key, value);
221 }
222
223 /**
224 * Convenient method for getting a path property that may contain the
225 * ${join.home} string. This latter will be replaced by its value.
226 * @param key Unique id of the application property
227 * @return The value of the corresponding application path property.
228 */
229 public String getPathProperty(String key){
230
231 String path = getProperty(key);
232 if (path == null) return null;
233
234 StringBuffer prop = new StringBuffer(path);
235 int length = "${join.home}".length();
236 for (int i=prop.indexOf("${join.home}"); i!=-1; i=prop.indexOf("${join.home}"))
237 prop.replace(i, i + length, getJoinHome());
238
239 return prop.toString();
240 }
241
242 /**
243 * Bootstrap the database access system. This method first test the access
244 * to database and them launch its configuration process within Hibernate.
245 * @param details Database security details wrapper
246 * @param embedded Tells if the db is the default embedded one
247 * @throws BootstrapException if database cannot be reached or configured
248 */
249 public void bootstrapDatabase(DatabaseDetails details, boolean embedded) throws BootstrapException{
250 try{
251
252
253 File database = new File(getJoinHome(), "database");
254 if (!database.isDirectory()) database.mkdir();
255
256 if (embedded){
257
258 String databasePath = getJoinHome() + "/database/joindb";
259 details.setDatabaseUrl("jdbc:hsqldb:" + databasePath);
260 log.debug("Database is embedded. Setting its Url to: " + details.getDatabaseUrl());
261 }
262
263 testDatabaseConnection(details);
264 hbnConfigurator.configureDatabase(details, embedded);
265 }
266 catch (Exception e){
267 log.fatal("Unable to bootstrap database: \n db: " + details + " \n embedded: " + embedded);
268 log.fatal("The exception message is: " + e.getMessage());
269 throw new BootstrapException("Unable to bootstrap database !");
270 }
271 launchConnectionKeeper();
272 }
273
274 /**
275 * Bootstrap the database access system through JNDI datasource. This method first
276 * test the access to datasource and them launch its configuration process within Hibernate.
277 * @param datasourceName JNDI name of datasource to use for accessing database
278 * @param dialect Dialect of database to access (as Hibernate dialect)
279 * @throws BootstrapException if database cannot be reached or configured
280 */
281 public void bootstrapDatasource(String datasourceName, String dialect) throws BootstrapException{
282 try{
283 log.debug("Using a DataSource for connecting to database: " + datasourceName);
284
285 testDatasourceAccess(datasourceName);
286 hbnConfigurator.configureDatasource(datasourceName, dialect);
287 }
288 catch (Exception e){
289 log.fatal("Unable to bootstrap on datasource: " + datasourceName + " with dialect " + dialect);
290 log.fatal("The exception message is: " + e.getMessage());
291 throw new BootstrapException("Unable to bootstrap on datasource !");
292 }
293 }
294
295 /**
296 * Bootstrap the messaging system. This method launches the configuration
297 * process within AcitveMQ.
298 * @param brokerUrl Messaging broker url
299 * @param embedded Tells if the messaging broker is on this side of application
300 * @throws BootstrapException if connection to messaging system cannot be configured
301 */
302 public void bootstrapMessaging(String brokerUrl, boolean embedded) throws BootstrapException{
303 try{
304 if (embedded && brokerUrl == null){
305
306 int port = 61626;
307 boolean found = false;
308 while (!found && port < 65635){
309 brokerUrl = "tcp://localhost:" + port;
310
311 try{
312 testBrokerConnection(new URI(brokerUrl));
313 found = true;
314 }
315 catch (Exception e) {port++;}
316 }
317 log.debug("Messaging broker is embedded. Setting its Url to: " + brokerUrl);
318 }
319 else{
320
321 testBrokerConnection(new URI(brokerUrl));
322 }
323
324 amqConfigurator.configureMessaging(brokerUrl);
325 }
326 catch (Exception e){
327 log.fatal("Unable to bootstrap messaging system");
328 log.fatal("The exception message is: " + e.getMessage());
329 throw new BootstrapException("Unable to bootstrap messaging system !");
330 }
331 }
332
333
334
335
336 /**
337 * Test the connection to configured database.
338 * @param details Database details wrapper for connection params
339 */
340 protected void testDatabaseConnection(DatabaseDetails details) throws Exception{
341 Connection conn = null;
342 log.info("Testing the connection to database...");
343
344 try{
345
346 Class.forName(details.getDriverClassname());
347 conn = DriverManager.getConnection(details.getDatabaseUrl(), details.getUsername(), details.getPassword());
348 log.info("Test connection has succeed !");
349 }
350 catch (Exception e){
351
352 log.error("Exception while testing database connection: " + e.getMessage());
353 e.printStackTrace();
354 }
355 finally{
356 try{
357
358 conn.close();
359 }
360 catch (Exception e){
361
362 log.error("The connection opened for testing database cannot be closed !");
363 throw e;
364 }
365 }
366 }
367
368 /**
369 * Test the access to configured datasource
370 * @param datasourceName JNDI name of datasource to access
371 */
372 protected void testDatasourceAccess(String datasourceName) throws Exception{
373 log.info("Testing the access to datasource...");
374
375 InitialContext ctx = new InitialContext();
376 Object obj = ctx.lookup(datasourceName);
377 DataSource ds = (DataSource)obj;
378 log.info("Test datasource has succeed ! (" + ds + ")");
379 }
380
381 /**
382 * Test the connection to broker denoted by <b>brokerUri</b>. If URI denotes
383 * localhost, we have to test broker from server side point of view (ie. using
384 * a ServerSocket).
385 * @param brokerUri The URI of broker to test connection to
386 */
387 protected void testBrokerConnection(URI brokerUri) throws Exception{
388 Socket client = null;
389 ServerSocket server = null;
390 log.info("Testing the connection to broker...");
391
392 try{
393
394 String host = brokerUri.getHost();
395 InetAddress addr = InetAddress.getByName(host);
396
397 if (host.trim().equals("localhost") || addr.equals(InetAddress.getByName("localhost"))){
398 server = new ServerSocket(brokerUri.getPort());
399 server.setReuseAddress(true);
400 }
401
402 else
403 client = new Socket(brokerUri.getHost(), brokerUri.getPort());
404 }
405 catch (Exception e){
406
407 log.error("Exception while testing broker connection: " + e.getMessage());
408 e.printStackTrace();
409 }
410 finally{
411 try{
412 if (client != null) client.close();
413 if (server != null) server.close();
414 }
415 catch (Exception e){
416
417 log.error("The connection opened for testing broker cannot be closed !");
418 throw e;}
419 }
420 }
421
422 /** Launch the ConnectionKeeper daemon. */
423 protected void launchConnectionKeeper(){
424 ConnectionKeeperLauncher.startConnectionKeeperIfNecessary();
425 }
426 }