View Javadoc
1   package org.apache.fulcrum.yaafi.cli;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  
24  import org.apache.avalon.framework.activity.Disposable;
25  import org.apache.avalon.framework.logger.ConsoleLogger;
26  import org.apache.avalon.framework.logger.Logger;
27  import org.apache.avalon.framework.service.ServiceManager;
28  import org.apache.fulcrum.yaafi.framework.container.ServiceContainer;
29  import org.apache.fulcrum.yaafi.framework.factory.ServiceContainerConfiguration;
30  import org.apache.fulcrum.yaafi.framework.factory.ServiceContainerFactory;
31  
32  /**
33   * An example of the embedding of a YAAFI kernel inside an
34   * arbitrary application.
35   */
36  
37  public class Main implements Runnable, Disposable
38  {
39      /** parameter for the application name */
40      public static final String APPLICATION_NAME = "yaafi.cli.applicationName";
41  
42      /** parameter for the application home directory */
43      public static final String APPLICATION_HOME = "yaafi.cli.applicationHome";
44  
45      /** parameter for the application temporary directory */
46      public static final String APPLICATION_TEMP = "yaafi.cli.tempHome";
47  
48      /** parameter for the application container configuration file */
49      public static final String APPLICATION_CONFIG = "yaafi.cli.config";
50  
51      /** parameter for setting a shutdown hook */
52      public static final String APPLICATION_HASSHUTDOWNHOOK = "yaafi.cli.hasShutdownHook";
53  
54      /** parameter for blocking the main thread in Main.run() */
55      public static final String APPLICATION_ISBLOCKING = "yaafi.cli.isBlocking";
56  
57      /** the interval to check for termination */
58      private static final int SLEEP_TIME = 100;
59  
60      /** the timeout for joining the shutdown thread */
61      private static final int JOIN_TIME = 1000;
62  
63      /** The service manager */
64      private ServiceContainer container;
65  
66      /** The location of the container configuration */
67      private String containerConfigValue;
68  
69      /** Thread for processing the shutdown notification of the JVM */
70      private Thread shutdownThread;
71  
72      /** Do we block the invoking thread until the JVM terminates ?! */
73      private boolean isBlocking;
74  
75      /** Do we install a shutdown hook for the JVM ?! */
76      private boolean hasShutdownHook;
77  
78      /** The logger being used */
79      private Logger logger;
80  
81      /** the name of the application */
82      private String applicationName;
83  
84      /** the working directory */
85      private String applicationHome;
86  
87      /** the temp directory */
88      private String tempHome;
89  
90      /** the command line arguments */
91      private String[] args;
92  
93      /** is the instance properly initialized */
94      private volatile boolean isInitialized;
95  
96  
97      /**
98       * Constructor
99       */
100     public Main()
101     {
102         // default initialization
103 
104         this.containerConfigValue   = "./conf/containerConfiguration.xml";
105         this.logger                 = new ConsoleLogger();
106         this.applicationHome        = ".";
107         this.tempHome               = System.getProperty("java.io.tmpdir",".");
108         this.applicationName        = "main";
109         
110         // Arguments are specified in the constructor, but if 
111         // null, set to an empty string array
112         if ( this.args == null ) { this.args = new String[0]; }
113 
114         this.isBlocking             = false;
115         this.hasShutdownHook        = true;
116         this.isInitialized          = false;
117 
118         // query the system properties
119 
120         this.containerConfigValue = System.getProperty(
121                 APPLICATION_CONFIG,
122                 this.containerConfigValue
123                 );
124 
125         this.applicationName = System.getProperty(
126                 APPLICATION_NAME,
127                 this.applicationName
128                 );
129 
130         this.applicationHome = System.getProperty(
131             APPLICATION_HOME,
132             this.applicationHome
133             );
134 
135         this.tempHome = System.getProperty(
136             APPLICATION_TEMP,
137             this.tempHome
138             );
139     }
140 
141     /**
142      * Constructor
143      *
144      * The following command line parameters are supported
145      * <ul>
146      *   <li>--yaafi.cli.applicationName name</li>
147      *   <li>--yaafi.cli.applicationHome dir</li>
148      *   <li>--yaafi.cli.tempHome dir</li>
149      *   <li>--yaafi.cli.isBlocking [true|false]</li>
150      *   <li>--yaafi.cli.hasShutdownHook [true|false]</li>
151      *   <li>--yaafi.cli.config file</li>
152      * </ul>
153      *
154      * @param args the command line arguments
155      */
156     public Main( String[] args )
157     {
158         this();
159 
160         this.args = args;
161 
162         // parse the command line
163 
164         Getopt getopt = new Getopt(this.args);
165 
166         this.setApplicationName(
167             getopt.getStringValue( APPLICATION_NAME, this.getApplicationName() )
168             );
169 
170         this.setApplicationHome(
171             getopt.getStringValue( APPLICATION_HOME, this.getApplicationHome() )
172             );
173 
174         this.setTempHome(
175             getopt.getStringValue( APPLICATION_TEMP, this.getTempHome() )
176             );
177 
178         this.setContainerConfigValue(
179             getopt.getStringValue( APPLICATION_CONFIG, this.getContainerConfigValue() )
180             );
181 
182         this.setIsBlocking(
183             getopt.getBooleanValue( APPLICATION_ISBLOCKING, this.isBlocking )
184             );
185 
186         this.setHasShutdownHook(
187             getopt.getBooleanValue( APPLICATION_HASSHUTDOWNHOOK, this.hasShutdownHook )
188             );
189     }
190 
191     /**
192      * The main method.
193      *
194      * @param args Command line arguments
195      * @throws Exception the execution failed
196      */
197     public static void main( String[] args ) throws Exception
198     {
199        int exitCode = 0;
200 
201        Main impl = new Main(args);
202 
203        try
204        {
205            impl.run();
206        }
207        catch (Throwable t)
208        {
209            exitCode = 1;
210        }
211 
212        System.exit(exitCode);
213     }
214 
215     /**
216      * Determines the file location of the given name. If the name denotes
217      * a relative file location it will be resolved using the application
218      * home directory.
219      *
220      * @param baseDir the base directory
221      * @param name the filename
222      * @return the file
223      */
224     public static File makeAbsoluteFile( File baseDir, String name )
225     {
226         File result = new File(name);
227 
228         if( !result.isAbsolute() )
229         {
230             result = new File( baseDir, name );
231         }
232 
233         return result;
234     }
235 
236     /**
237      * Dispose the YAAFI container
238      */
239 
240     public synchronized void dispose()
241     {
242         this.shutdown();
243     }
244 
245     /**
246      * Runs the instance by initializing it and potentially blocking
247      * the invoking thread depending on the configuration.
248      *
249      * @see java.lang.Runnable#run()
250      */
251     public void run()
252     {
253         try
254         {
255             this.initialize();
256             this.onWait();
257         }
258         catch (Throwable t)
259         {
260             String msg = "Failed to run " + this.getClass().getName();
261             this.getLogger().error(msg,t);
262             throw new RuntimeException(t.getMessage());
263         }
264     }
265 
266     /**
267      * Depending on the configuration this method might block
268      * the calling thread or return immediately. We currently
269      * poll a volatile variable which is not the most elegant
270      * solution.
271      */
272     public void onWait()
273     {
274         while( this.isBlocking() && this.isInitialized() )
275         {
276             try
277             {
278                 Thread.sleep(Main.SLEEP_TIME);
279             }
280             catch (InterruptedException e)
281             {
282                 // ignore
283             }
284         }
285     }
286 
287     /**
288      * Locates the file for the given file name.
289      * @param fileName the filename
290      * @return an absolute file
291      */
292     public File makeAbsoluteFile( String fileName )
293     {
294         return Main.makeAbsoluteFile(
295             new File(this.getApplicationHome()),
296             fileName
297             );
298     }
299 
300     /**
301      * Locates the file for the given file name.
302      * @param fileName the filename
303      * @return an absolute path
304      */
305     public String makeAbsolutePath( String fileName )
306     {
307         return Main.makeAbsoluteFile(
308             new File(this.getApplicationHome()),
309             fileName
310             ).getAbsolutePath();
311     }
312 
313     /////////////////////////////////////////////////////////////////////////
314     // Generated getters & setters
315     /////////////////////////////////////////////////////////////////////////
316 
317     /**
318      * @return Returns the ServiceContainer interface
319      */
320     public ServiceContainer getServiceContainer()
321     {
322         return this.container;
323     }
324 
325     /**
326      * @return Returns the ServiceManager interface
327      */
328     public ServiceManager getServiceManager()
329     {
330         return this.container;
331     }
332 
333     /**
334      * @return Returns the applicationHome.
335      */
336     public String getApplicationHome()
337     {
338         return this.applicationHome;
339     }
340 
341     /**
342      * @param applicationHome The applicationHome to set.
343      */
344     public void setApplicationHome(String applicationHome)
345     {
346         this.applicationHome = applicationHome;
347     }
348 
349     /**
350      * @return Returns the containerConfigValue.
351      */
352     public String getContainerConfigValue()
353     {
354         return containerConfigValue;
355     }
356 
357     /**
358      * @param containerConfigValue The containerConfigValue to set.
359      */
360     public void setContainerConfigValue(String containerConfigValue)
361     {
362         this.containerConfigValue = containerConfigValue;
363     }
364 
365     /**
366      * @return Returns the isBlocking.
367      */
368     public boolean isBlocking()
369     {
370         return isBlocking;
371     }
372 
373     /**
374      * @param isBlocking The isBlocking to set.
375      */
376     public void setIsBlocking(boolean isBlocking)
377     {
378         this.isBlocking = isBlocking;
379     }
380 
381     /**
382      * @param isBlocking The isBlocking to set.
383      */
384     public void setIsBlocking(Boolean isBlocking)
385     {
386         this.isBlocking = isBlocking.booleanValue();
387     }
388 
389     /**
390      * @param isBlocking The isBlocking to set.
391      */
392     public void setIsBlocking(String isBlocking)
393     {
394         this.isBlocking = Boolean.valueOf(isBlocking).booleanValue();
395     }
396 
397     /**
398      * @return Returns the tempHome.
399      */
400     public String getTempHome()
401     {
402         return this.tempHome;
403     }
404 
405     /**
406      * @param tempHome The tempHome to set.
407      */
408     public void setTempHome(String tempHome)
409     {
410         this.tempHome = tempHome;
411     }
412 
413     /**
414      * @return Returns the logger.
415      */
416     public Logger getLogger()
417     {
418         return this.logger;
419     }
420 
421     /**
422      * @param logger The logger to set.
423      */
424     public void setLogger(Logger logger)
425     {
426         this.logger = logger;
427     }
428 
429     /**
430      * @return Returns the applicationName.
431      */
432     public String getApplicationName()
433     {
434         return applicationName;
435     }
436 
437     /**
438      * @param applicationName The applicationName to set.
439      */
440     public void setApplicationName(String applicationName)
441     {
442         this.applicationName = applicationName;
443     }
444 
445     /**
446      * @return Returns the args.
447      */
448     public String [] getArgs()
449     {
450         return args;
451     }
452     /**
453      * @param args The args to set.
454      */
455     public void setArgs(String [] args)
456     {
457         this.args = args;
458     }
459 
460     /**
461      * @return Returns the hasShutdownHook.
462      */
463     public boolean hasShutdownHook()
464     {
465         return hasShutdownHook;
466     }
467 
468     /**
469      * @param hasShutdownHook The hasShutdownHook to set.
470      */
471     public void setHasShutdownHook(boolean hasShutdownHook)
472     {
473         this.hasShutdownHook = hasShutdownHook;
474     }
475 
476     /**
477      * @param hasShutdownHook The hasShutdownHook to set.
478      */
479     public void setHasShutdownHook(Boolean hasShutdownHook)
480     {
481         this.hasShutdownHook = hasShutdownHook.booleanValue();
482     }
483 
484     /**
485      * @param hasShutdownHook The hasShutdownHook to set.
486      */
487     public void setHasShutdownHook(String hasShutdownHook)
488     {
489         this.hasShutdownHook = Boolean.valueOf(hasShutdownHook).booleanValue();
490     }
491 
492     /**
493      * @see java.lang.Object#toString()
494      */
495     public String toString()
496     {
497         StringBuilder result = new StringBuilder();
498         StringBuilder argsLine = new StringBuilder();
499 
500         result.append(getClass().getName() + "@" + Integer.toHexString(hashCode()));
501 
502         result.append('[');
503         result.append("workingDir=" + new File("").getAbsolutePath());
504         result.append(',');
505 
506         result.append("args=");
507 
508         for( int i=0; i<this.getArgs().length; i++ )
509         {
510             argsLine.append( this.getArgs()[i] );
511 
512             if( (i+1) < this.getArgs().length )
513             {
514                 argsLine.append( " " );
515             }
516         }
517 
518         result.append( argsLine.toString() );
519         result.append(',');
520 
521         result.append("applicationName=" + this.getApplicationName());
522         result.append(',');
523         result.append("applicationHome=" + this.getApplicationHome());
524         result.append(',');
525         result.append("tempHome=" + this.getTempHome());
526         result.append(',');
527         result.append("logger=" + this.getLogger().getClass().getName());
528         result.append(',');
529         result.append("isBlocking=" + this.isBlocking);
530         result.append(',');
531         result.append("hasShutdownHook=" + this.hasShutdownHook());
532         result.append(',');
533         result.append("containerConfigValue=" + this.getContainerConfigValue());
534         result.append(']');
535 
536         return result.toString();
537     }
538 
539     /**
540      * @return Returns the isInitialized.
541      */
542     public boolean isInitialized()
543     {
544         return isInitialized;
545     }
546 
547     /////////////////////////////////////////////////////////////////////////
548     // Implementation
549     /////////////////////////////////////////////////////////////////////////
550 
551     /**
552      * @param isInitialized The isInitialized to set.
553      */
554     protected void setInitialized(boolean isInitialized)
555     {
556         this.isInitialized = isInitialized;
557     }
558 
559     /**
560      * Initialize the instance
561      *
562      * @throws Exception the initialization failed
563      */
564     public void initialize() throws Exception
565     {
566         this.getLogger().debug( "Initializing " + this.getClass().getName() );
567 
568         ServiceContainerConfiguration config = new ServiceContainerConfiguration();
569 
570         // initialize the Avalon container
571 
572         config.setLogger( this.getLogger() );
573         config.setApplicationRootDir( this.getApplicationHome() );
574         config.setTempRootDir( this.getTempHome() );
575         config.loadContainerConfiguration( this.getContainerConfigValue(), "auto" );
576 
577         this.container = ServiceContainerFactory.create( config );
578 
579         // initialize shutdown hook of JVM for a server application
580 
581         if( this.hasShutdownHook() )
582         {
583             this.getLogger().debug( "Registering shutdown hook" );
584             Shutdown shutdown = new Shutdown( this );
585             this.shutdownThread = new Thread( shutdown, "ShutdownThread" );
586             Runtime.getRuntime().addShutdownHook( this.shutdownThread );
587         }
588 
589         this.setInitialized(true);
590     }
591 
592     /**
593      * Terminates the instance
594      */
595     protected void shutdown()
596     {
597         if( !this.isInitialized())
598         {
599             return;
600         }
601 
602         this.getLogger().debug( "Terminating " + this.getClass().getName() );
603 
604         try
605         {
606             // wait for the shutdown thread
607 
608             if( this.shutdownThread != null )
609             {
610                 try
611                 {
612                     this.getLogger().debug( "Waiting for shutdown handler thread to terminate" );
613                     this.shutdownThread.join(JOIN_TIME);
614                     this.shutdownThread = null;
615                     this.getLogger().debug( "Shutdown handler thread is terminated" );
616                 }
617                 catch (InterruptedException e)
618                 {
619                     // nothing to do
620                 }
621             }
622 
623             // dispose the service container
624 
625             if( this.getServiceContainer() != null )
626             {
627                 this.getServiceContainer().dispose();
628                 this.container = null;
629             }
630 
631             this.setInitialized(false);
632         }
633 
634         catch (Exception e)
635         {
636             String msg = "Failed to terminate " + this.getClass().getName();
637             this.getLogger().error(msg,e);
638         }
639     }
640 }