001 package org.apache.fulcrum.yaafi.cli;
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 java.io.File;
023
024 import org.apache.avalon.framework.activity.Disposable;
025 import org.apache.avalon.framework.logger.ConsoleLogger;
026 import org.apache.avalon.framework.logger.Logger;
027 import org.apache.avalon.framework.service.ServiceManager;
028 import org.apache.fulcrum.yaafi.framework.container.ServiceContainer;
029 import org.apache.fulcrum.yaafi.framework.factory.ServiceContainerConfiguration;
030 import org.apache.fulcrum.yaafi.framework.factory.ServiceContainerFactory;
031
032
033 /**
034 * An example of the embedding of a YAAFI kernel inside an
035 * arbitrary application.
036 */
037
038 public class Main implements Runnable, Disposable
039 {
040 /** parameter for the application name */
041 public static final String APPLICATION_NAME = "yaafi.cli.applicationName";
042
043 /** parameter for the application home directory */
044 public static final String APPLICATION_HOME = "yaafi.cli.applicationHome";
045
046 /** parameter for the application temporary directory */
047 public static final String APPLICATION_TEMP = "yaafi.cli.tempHome";
048
049 /** parameter for the application container configuration file */
050 public static final String APPLICATION_CONFIG = "yaafi.cli.config";
051
052 /** parameter for setting a shutdown hook */
053 public static final String APPLICATION_HASSHUTDOWNHOOK = "yaafi.cli.hasShutdownHook";
054
055 /** parameter for blocking the main thread in Main.run() */
056 public static final String APPLICATION_ISBLOCKING = "yaafi.cli.isBlocking";
057
058 /** the interval to check for termination */
059 private static final int SLEEP_TIME = 100;
060
061 /** the timeout for joing the shutdown thread */
062 private static final int JOIN_TIME = 1000;
063
064 /** The service manager */
065 private ServiceContainer container;
066
067 /** The location of the container configuration */
068 private String containerConfigValue;
069
070 /** Thread for processing the shutdown notification of the JVM */
071 private Thread shutdownThread;
072
073 /** Do we block the invoking thread until the JVM terminates ?! */
074 private boolean isBlocking;
075
076 /** Do we install a shutdown hook for the JVM ?! */
077 private boolean hasShutdownHook;
078
079 /** The logger being used */
080 private Logger logger;
081
082 /** the name of the application */
083 private String applicationName;
084
085 /** the working directory */
086 private String applicationHome;
087
088 /** the temp directory */
089 private String tempHome;
090
091 /** the command line arguments */
092 private String[] args;
093
094 /** is the instance properly initialized */
095 private volatile boolean isInitialized;
096
097
098 /**
099 * Constructor
100 */
101 public Main()
102 {
103 // default initialization
104
105 this.containerConfigValue = "./conf/containerConfiguration.xml";
106 this.logger = new ConsoleLogger();
107 this.applicationHome = ".";
108 this.tempHome = System.getProperty("java.io.tmpdir",".");
109 this.applicationName = "main";
110 this.args = ( args != null ? args : new String[0] );
111 this.isBlocking = false;
112 this.hasShutdownHook = true;
113 this.isInitialized = false;
114
115 // query the system properties
116
117 this.containerConfigValue = System.getProperty(
118 APPLICATION_CONFIG,
119 this.containerConfigValue
120 );
121
122 this.applicationName = System.getProperty(
123 APPLICATION_NAME,
124 this.applicationName
125 );
126
127 this.applicationHome = System.getProperty(
128 APPLICATION_HOME,
129 this.applicationHome
130 );
131
132 this.tempHome = System.getProperty(
133 APPLICATION_TEMP,
134 this.tempHome
135 );
136 }
137
138 /**
139 * Constructor
140 *
141 * The following command line parameters are supported
142 * <ul>
143 * <li>--yaafi.cli.applicationName name</li>
144 * <li>--yaafi.cli.applicationHome dir</li>
145 * <li>--yaafi.cli.tempHome dir</li>
146 * <li>--yaafi.cli.isBlocking [true|false]</li>
147 * <li>--yaafi.cli.hasShutdownHook [true|false]</li>
148 * <li>--yaafi.cli.config file</li>
149 * </ul>
150 *
151 * @param args the command line arguments
152 */
153 public Main( String[] args )
154 {
155 this();
156
157 this.args = args;
158
159 // parse the command line
160
161 Getopt getopt = new Getopt(this.args);
162
163 this.setApplicationName(
164 getopt.getStringValue( APPLICATION_NAME, this.getApplicationName() )
165 );
166
167 this.setApplicationHome(
168 getopt.getStringValue( APPLICATION_HOME, this.getApplicationHome() )
169 );
170
171 this.setTempHome(
172 getopt.getStringValue( APPLICATION_TEMP, this.getTempHome() )
173 );
174
175 this.setContainerConfigValue(
176 getopt.getStringValue( APPLICATION_CONFIG, this.getContainerConfigValue() )
177 );
178
179 this.setIsBlocking(
180 getopt.getBooleanValue( APPLICATION_ISBLOCKING, this.isBlocking )
181 );
182
183 this.setHasShutdownHook(
184 getopt.getBooleanValue( APPLICATION_HASSHUTDOWNHOOK, this.hasShutdownHook )
185 );
186 }
187
188 /**
189 * The main method.
190 *
191 * @param args Command line arguments
192 * @throws Exception the execution failed
193 */
194 public static void main( String[] args ) throws Exception
195 {
196 int exitCode = 0;
197
198 Main impl = new Main(args);
199
200 try
201 {
202 impl.run();
203 }
204 catch (Throwable t)
205 {
206 exitCode = 1;
207 }
208
209 System.exit(exitCode);
210 }
211
212 /**
213 * Determines the file location of the given name. If the name denotes
214 * a relative file location it will be rsolved using the application
215 * home directory.
216 *
217 * @param baseDir the base directory
218 * @param name the filename
219 * @return the file
220 */
221 public static File makeAbsoluteFile( File baseDir, String name )
222 {
223 File result = new File(name);
224
225 if( !result.isAbsolute() )
226 {
227 result = new File( baseDir, name );
228 }
229
230 return result;
231 }
232
233 /**
234 * Dispose the YAAFI container
235 */
236
237 public synchronized void dispose()
238 {
239 this.shutdown();
240 }
241
242 /**
243 * Runs the instance by initializing it and potentially blocking
244 * the invoking thread depending on the configuration.
245 *
246 * @see java.lang.Runnable#run()
247 */
248 public void run()
249 {
250 try
251 {
252 this.initialize();
253 this.onWait();
254 }
255 catch (Throwable t)
256 {
257 String msg = "Failed to run " + this.getClass().getName();
258 this.getLogger().error(msg,t);
259 throw new RuntimeException(t.getMessage());
260 }
261 }
262
263 /**
264 * Depending on the configuration this method might block
265 * the calling thread or return immediatly. We currently
266 * poll a volatile variable which is not the most elegant
267 * solution.
268 */
269 public void onWait()
270 {
271 while( this.isBlocking() && this.isInitialized() )
272 {
273 try
274 {
275 Thread.sleep(Main.SLEEP_TIME);
276 }
277 catch (InterruptedException e)
278 {
279 // ignore
280 }
281 }
282 }
283
284 /**
285 * Locates the file for the given file name.
286 * @param fileName the filename
287 * @return an absolute file
288 */
289 public File makeAbsoluteFile( String fileName )
290 {
291 return Main.makeAbsoluteFile(
292 new File(this.getApplicationHome()),
293 fileName
294 );
295 }
296
297 /**
298 * Locates the file for the given file name.
299 * @param fileName the filename
300 * @return an absolute path
301 */
302 public String makeAbsolutePath( String fileName )
303 {
304 return Main.makeAbsoluteFile(
305 new File(this.getApplicationHome()),
306 fileName
307 ).getAbsolutePath();
308 }
309
310 /////////////////////////////////////////////////////////////////////////
311 // Generated getters & setters
312 /////////////////////////////////////////////////////////////////////////
313
314 /**
315 * @return Returns the ServiceContainer interface
316 */
317 public ServiceContainer getServiceContainer()
318 {
319 return this.container;
320 }
321
322 /**
323 * @return Returns the ServiceManager interface
324 */
325 public ServiceManager getServiceManager()
326 {
327 return this.container;
328 }
329
330 /**
331 * @return Returns the applicationHome.
332 */
333 public String getApplicationHome()
334 {
335 return this.applicationHome;
336 }
337
338 /**
339 * @param applicationHome The applicationHome to set.
340 */
341 public void setApplicationHome(String applicationHome)
342 {
343 this.applicationHome = applicationHome;
344 }
345
346 /**
347 * @return Returns the containerConfigValue.
348 */
349 public String getContainerConfigValue()
350 {
351 return containerConfigValue;
352 }
353
354 /**
355 * @param containerConfigValue The containerConfigValue to set.
356 */
357 public void setContainerConfigValue(String containerConfigValue)
358 {
359 this.containerConfigValue = containerConfigValue;
360 }
361
362 /**
363 * @return Returns the isBlocking.
364 */
365 public boolean isBlocking()
366 {
367 return isBlocking;
368 }
369
370 /**
371 * @param isBlocking The isBlocking to set.
372 */
373 public void setIsBlocking(boolean isBlocking)
374 {
375 this.isBlocking = isBlocking;
376 }
377
378 /**
379 * @param isBlocking The isBlocking to set.
380 */
381 public void setIsBlocking(Boolean isBlocking)
382 {
383 this.isBlocking = isBlocking.booleanValue();
384 }
385
386 /**
387 * @param isBlocking The isBlocking to set.
388 */
389 public void setIsBlocking(String isBlocking)
390 {
391 this.isBlocking = Boolean.valueOf(isBlocking).booleanValue();
392 }
393
394 /**
395 * @return Returns the tempHome.
396 */
397 public String getTempHome()
398 {
399 return this.tempHome;
400 }
401
402 /**
403 * @param tempHome The tempHome to set.
404 */
405 public void setTempHome(String tempHome)
406 {
407 this.tempHome = tempHome;
408 }
409
410 /**
411 * @return Returns the logger.
412 */
413 public Logger getLogger()
414 {
415 return this.logger;
416 }
417
418 /**
419 * @param logger The logger to set.
420 */
421 public void setLogger(Logger logger)
422 {
423 this.logger = logger;
424 }
425
426 /**
427 * @return Returns the applicationName.
428 */
429 public String getApplicationName()
430 {
431 return applicationName;
432 }
433
434 /**
435 * @param applicationName The applicationName to set.
436 */
437 public void setApplicationName(String applicationName)
438 {
439 this.applicationName = applicationName;
440 }
441
442 /**
443 * @return Returns the args.
444 */
445 public String [] getArgs()
446 {
447 return args;
448 }
449 /**
450 * @param args The args to set.
451 */
452 public void setArgs(String [] args)
453 {
454 this.args = args;
455 }
456
457 /**
458 * @return Returns the hasShutdownHook.
459 */
460 public boolean hasShutdownHook()
461 {
462 return hasShutdownHook;
463 }
464
465 /**
466 * @param hasShutdownHook The hasShutdownHook to set.
467 */
468 public void setHasShutdownHook(boolean hasShutdownHook)
469 {
470 this.hasShutdownHook = hasShutdownHook;
471 }
472
473 /**
474 * @param hasShutdownHook The hasShutdownHook to set.
475 */
476 public void setHasShutdownHook(Boolean hasShutdownHook)
477 {
478 this.hasShutdownHook = hasShutdownHook.booleanValue();
479 }
480
481 /**
482 * @param hasShutdownHook The hasShutdownHook to set.
483 */
484 public void setHasShutdownHook(String hasShutdownHook)
485 {
486 this.hasShutdownHook = Boolean.valueOf(hasShutdownHook).booleanValue();
487 }
488
489 /**
490 * @see java.lang.Object#toString()
491 */
492 public String toString()
493 {
494 StringBuffer result = new StringBuffer();
495 StringBuffer argsLine = new StringBuffer();
496
497 result.append(getClass().getName() + "@" + Integer.toHexString(hashCode()));
498
499 result.append('[');
500 result.append("workingDir=" + new File("").getAbsolutePath());
501 result.append(',');
502
503 result.append("args=");
504
505 for( int i=0; i<this.getArgs().length; i++ )
506 {
507 argsLine.append( this.getArgs()[i] );
508
509 if( (i+1) < this.getArgs().length )
510 {
511 argsLine.append( " " );
512 }
513 }
514
515 result.append( argsLine.toString() );
516 result.append(',');
517
518 result.append("applicationName=" + this.getApplicationName());
519 result.append(',');
520 result.append("applicationHome=" + this.getApplicationHome());
521 result.append(',');
522 result.append("tempHome=" + this.getTempHome());
523 result.append(',');
524 result.append("logger=" + this.getLogger().getClass().getName());
525 result.append(',');
526 result.append("isBlocking=" + this.isBlocking);
527 result.append(',');
528 result.append("hasShutdownHook=" + this.hasShutdownHook());
529 result.append(',');
530 result.append("containerConfigValue=" + this.getContainerConfigValue());
531 result.append(']');
532
533 return result.toString();
534 }
535
536 /**
537 * @return Returns the isInitialized.
538 */
539 public boolean isInitialized()
540 {
541 return isInitialized;
542 }
543
544 /////////////////////////////////////////////////////////////////////////
545 // Implementation
546 /////////////////////////////////////////////////////////////////////////
547
548 /**
549 * @param isInitialized The isInitialized to set.
550 */
551 protected void setInitialized(boolean isInitialized)
552 {
553 this.isInitialized = isInitialized;
554 }
555
556 /**
557 * Initialize the instance
558 *
559 * @throws Exception the initialization failed
560 */
561 public void initialize() throws Exception
562 {
563 this.getLogger().debug( "Initializing " + this.getClass().getName() );
564
565 ServiceContainerConfiguration config = new ServiceContainerConfiguration();
566
567 // intialize the Avalon container
568
569 config.setLogger( this.getLogger() );
570 config.setApplicationRootDir( this.getApplicationHome() );
571 config.setTempRootDir( this.getTempHome() );
572 config.loadContainerConfiguration( this.getContainerConfigValue(), "auto" );
573
574 this.container = ServiceContainerFactory.create( config );
575
576 // initialize shutdown hook of JVM for a server application
577
578 if( this.hasShutdownHook() )
579 {
580 this.getLogger().debug( "Registering shutdown hook" );
581 Shutdown shutdown = new Shutdown( this );
582 this.shutdownThread = new Thread( shutdown, "ShutdownThread" );
583 Runtime.getRuntime().addShutdownHook( this.shutdownThread );
584 }
585
586 this.setInitialized(true);
587 }
588
589 /**
590 * Terminates the instance
591 */
592 protected void shutdown()
593 {
594 if( !this.isInitialized())
595 {
596 return;
597 }
598
599 this.getLogger().debug( "Terminating " + this.getClass().getName() );
600
601 try
602 {
603 // wait for the shutdown thread
604
605 if( this.shutdownThread != null )
606 {
607 try
608 {
609 this.getLogger().debug( "Waiting for shutdown handler thread to terminate" );
610 this.shutdownThread.join(JOIN_TIME);
611 this.shutdownThread = null;
612 this.getLogger().debug( "Shutdown handler thread is terminated" );
613 }
614 catch (InterruptedException e)
615 {
616 // nothing to do
617 }
618 }
619
620 // dispose the service container
621
622 if( this.getServiceContainer() != null )
623 {
624 this.getServiceContainer().dispose();
625 this.container = null;
626 }
627
628 this.setInitialized(false);
629 }
630
631 catch (Exception e)
632 {
633 String msg = "Failed to terminate " + this.getClass().getName();
634 this.getLogger().error(msg,e);
635 }
636 }
637 }