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  /**
34   * An example of the embedding of a YAAFI kernel inside an
35   * arbitrary application.
36   */
37  
38  public class Main implements Runnable, Disposable
39  {
40      /** parameter for the application name */
41      public static final String APPLICATION_NAME = "yaafi.cli.applicationName";
42  
43      /** parameter for the application home directory */
44      public static final String APPLICATION_HOME = "yaafi.cli.applicationHome";
45  
46      /** parameter for the application temporary directory */
47      public static final String APPLICATION_TEMP = "yaafi.cli.tempHome";
48  
49      /** parameter for the application container configuration file */
50      public static final String APPLICATION_CONFIG = "yaafi.cli.config";
51  
52      /** parameter for setting a shutdown hook */
53      public static final String APPLICATION_HASSHUTDOWNHOOK = "yaafi.cli.hasShutdownHook";
54  
55      /** parameter for blocking the main thread in Main.run() */
56      public static final String APPLICATION_ISBLOCKING = "yaafi.cli.isBlocking";
57  
58      /** the interval to check for termination */
59      private static final int SLEEP_TIME = 100;
60  
61      /** the timeout for joing the shutdown thread */
62      private static final int JOIN_TIME = 1000;
63  
64      /** The service manager */
65      private ServiceContainer container;
66  
67      /** The location of the container configuration */
68      private String containerConfigValue;
69  
70      /** Thread for processing the shutdown notification of the JVM */
71      private Thread shutdownThread;
72  
73      /** Do we block the invoking thread until the JVM terminates ?! */
74      private boolean isBlocking;
75  
76      /** Do we install a shutdown hook for the JVM ?! */
77      private boolean hasShutdownHook;
78  
79      /** The logger being used */
80      private Logger logger;
81  
82      /** the name of the application */
83      private String applicationName;
84  
85      /** the working directory */
86      private String applicationHome;
87  
88      /** the temp directory */
89      private String tempHome;
90  
91      /** the command line arguments */
92      private String[] args;
93  
94      /** is the instance properly initialized */
95      private volatile boolean isInitialized;
96  
97  
98      /**
99       * 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        finally
209        {
210            System.exit(exitCode);
211        }
212     }
213 
214     /**
215      * Determines the file location of the given name. If the name denotes
216      * a relative file location it will be rsolved using the application
217      * home directory.
218      *
219      * @param baseDir the base directory
220      * @param name the filename
221      * @return the file
222      */
223     public static File makeAbsoluteFile( File baseDir, String name )
224     {
225         File result = new File(name);
226 
227         if( result.isAbsolute() == false )
228         {
229             result = new File( baseDir, name );
230         }
231 
232         return result;
233     }
234 
235     /**
236      * Dispose the YAAFI container
237      */
238 
239     public synchronized void dispose()
240     {
241         this.shutdown();
242     }
243 
244     /**
245      * Runs the instance by initializing it and potentially blocking
246      * the invoking thread depending on the configuration.
247      *
248      * @see java.lang.Runnable#run()
249      */
250     public void run()
251     {
252         try
253         {
254             this.initialize();
255             this.onWait();
256         }
257         catch (Throwable t)
258         {
259             String msg = "Failed to run " + this.getClass().getName();
260             this.getLogger().error(msg,t);
261             throw new RuntimeException(t.getMessage());
262         }
263     }
264 
265     /**
266      * Depending on the configuration this method might block
267      * the calling thread or return immediatly. We currently
268      * poll a volatile variable which is not the most elegant
269      * solution.
270      */
271     public void onWait()
272     {
273         while( this.isBlocking() && this.isInitialized() )
274         {
275             try
276             {
277                 Thread.sleep(Main.SLEEP_TIME);
278             }
279             catch (InterruptedException e)
280             {
281                 // ignore
282             }
283         }
284     }
285 
286     /**
287      * Locates the file for the given file name.
288      * @param fileName the filename
289      * @return an absolute file
290      */
291     public File makeAbsoluteFile( String fileName )
292     {
293         return Main.makeAbsoluteFile(
294             new File(this.getApplicationHome()),
295             fileName
296             );
297     }
298 
299     /**
300      * Locates the file for the given file name.
301      * @param fileName the filename
302      * @return an absolute path
303      */
304     public String makeAbsolutePath( String fileName )
305     {
306         return Main.makeAbsoluteFile(
307             new File(this.getApplicationHome()),
308             fileName
309             ).getAbsolutePath();
310     }
311 
312     /////////////////////////////////////////////////////////////////////////
313     // Generated getters & setters
314     /////////////////////////////////////////////////////////////////////////
315 
316     /**
317      * @return Returns the ServiceContainer interface
318      */
319     public ServiceContainer getServiceContainer()
320     {
321         return this.container;
322     }
323 
324     /**
325      * @return Returns the ServiceManager interface
326      */
327     public ServiceManager getServiceManager()
328     {
329         return this.container;
330     }
331 
332     /**
333      * @return Returns the applicationHome.
334      */
335     public String getApplicationHome()
336     {
337         return this.applicationHome;
338     }
339 
340     /**
341      * @param applicationHome The applicationHome to set.
342      */
343     public void setApplicationHome(String applicationHome)
344     {
345         this.applicationHome = applicationHome;
346     }
347 
348     /**
349      * @return Returns the containerConfigValue.
350      */
351     public String getContainerConfigValue()
352     {
353         return containerConfigValue;
354     }
355 
356     /**
357      * @param containerConfigValue The containerConfigValue to set.
358      */
359     public void setContainerConfigValue(String containerConfigValue)
360     {
361         this.containerConfigValue = containerConfigValue;
362     }
363 
364     /**
365      * @return Returns the isBlocking.
366      */
367     public boolean isBlocking()
368     {
369         return isBlocking;
370     }
371 
372     /**
373      * @param isBlocking The isBlocking to set.
374      */
375     public void setIsBlocking(boolean isBlocking)
376     {
377         this.isBlocking = isBlocking;
378     }
379 
380     /**
381      * @param isBlocking The isBlocking to set.
382      */
383     public void setIsBlocking(Boolean isBlocking)
384     {
385         this.isBlocking = isBlocking.booleanValue();
386     }
387 
388     /**
389      * @param isBlocking The isBlocking to set.
390      */
391     public void setIsBlocking(String isBlocking)
392     {
393         this.isBlocking = Boolean.valueOf(isBlocking).booleanValue();
394     }
395 
396     /**
397      * @return Returns the tempHome.
398      */
399     public String getTempHome()
400     {
401         return this.tempHome;
402     }
403 
404     /**
405      * @param tempHome The tempHome to set.
406      */
407     public void setTempHome(String tempHome)
408     {
409         this.tempHome = tempHome;
410     }
411 
412     /**
413      * @return Returns the logger.
414      */
415     public Logger getLogger()
416     {
417         return this.logger;
418     }
419 
420     /**
421      * @param logger The logger to set.
422      */
423     public void setLogger(Logger logger)
424     {
425         this.logger = logger;
426     }
427 
428     /**
429      * @return Returns the applicationName.
430      */
431     public String getApplicationName()
432     {
433         return applicationName;
434     }
435 
436     /**
437      * @param applicationName The applicationName to set.
438      */
439     public void setApplicationName(String applicationName)
440     {
441         this.applicationName = applicationName;
442     }
443 
444     /**
445      * @return Returns the args.
446      */
447     public String [] getArgs()
448     {
449         return args;
450     }
451     /**
452      * @param args The args to set.
453      */
454     public void setArgs(String [] args)
455     {
456         this.args = args;
457     }
458 
459     /**
460      * @return Returns the hasShutdownHook.
461      */
462     public boolean hasShutdownHook()
463     {
464         return hasShutdownHook;
465     }
466 
467     /**
468      * @param hasShutdownHook The hasShutdownHook to set.
469      */
470     public void setHasShutdownHook(boolean hasShutdownHook)
471     {
472         this.hasShutdownHook = hasShutdownHook;
473     }
474 
475     /**
476      * @param hasShutdownHook The hasShutdownHook to set.
477      */
478     public void setHasShutdownHook(Boolean hasShutdownHook)
479     {
480         this.hasShutdownHook = hasShutdownHook.booleanValue();
481     }
482 
483     /**
484      * @param hasShutdownHook The hasShutdownHook to set.
485      */
486     public void setHasShutdownHook(String hasShutdownHook)
487     {
488         this.hasShutdownHook = Boolean.valueOf(hasShutdownHook).booleanValue();
489     }
490 
491     /**
492      * @see java.lang.Object#toString()
493      */
494     public String toString()
495     {
496         StringBuffer result = new StringBuffer();
497         StringBuffer argsLine = new StringBuffer();
498 
499         result.append(getClass().getName() + "@" + Integer.toHexString(hashCode()));
500 
501         result.append('[');
502         result.append("workingDir=" + new File("").getAbsolutePath());
503         result.append(',');
504 
505         result.append("args=");
506 
507         for( int i=0; i<this.getArgs().length; i++ )
508         {
509             argsLine.append( this.getArgs()[i] );
510 
511             if( (i+1) < this.getArgs().length )
512             {
513                 argsLine.append( " " );
514             }
515         }
516 
517         result.append( argsLine.toString() );
518         result.append(',');
519 
520         result.append("applicationName=" + this.getApplicationName());
521         result.append(',');
522         result.append("applicationHome=" + this.getApplicationHome());
523         result.append(',');
524         result.append("tempHome=" + this.getTempHome());
525         result.append(',');
526         result.append("logger=" + this.getLogger().getClass().getName());
527         result.append(',');
528         result.append("isBlocking=" + this.isBlocking);
529         result.append(',');
530         result.append("hasShutdownHook=" + this.hasShutdownHook());
531         result.append(',');
532         result.append("containerConfigValue=" + this.getContainerConfigValue());
533         result.append(']');
534 
535         return result.toString();
536     }
537 
538     /**
539      * @return Returns the isInitialized.
540      */
541     public boolean isInitialized()
542     {
543         return isInitialized;
544     }
545 
546     /////////////////////////////////////////////////////////////////////////
547     // Implementation
548     /////////////////////////////////////////////////////////////////////////
549 
550     /**
551      * @param isInitialized The isInitialized to set.
552      */
553     protected void setInitialized(boolean isInitialized)
554     {
555         this.isInitialized = isInitialized;
556     }
557 
558     /**
559      * Initialize the instance
560      *
561      * @throws Exception the initialization failed
562      */
563     public void initialize() throws Exception
564     {
565         this.getLogger().debug( "Initializing " + this.getClass().getName() );
566 
567         ServiceContainerConfiguration config = new ServiceContainerConfiguration();
568 
569         // intialize the Avalon container
570 
571         config.setLogger( this.getLogger() );
572         config.setApplicationRootDir( this.getApplicationHome() );
573         config.setTempRootDir( this.getTempHome() );
574         config.loadContainerConfiguration( this.getContainerConfigValue(), "auto" );
575 
576         this.container = ServiceContainerFactory.create( config );
577 
578         // initialize shutdown hook of JVM for a server application
579 
580         if( this.hasShutdownHook() )
581         {
582             this.getLogger().debug( "Registering shutdown hook" );
583             Shutdown shutdown = new Shutdown( this );
584             this.shutdownThread = new Thread( shutdown, "ShutdownThread" );
585             Runtime.getRuntime().addShutdownHook( this.shutdownThread );
586         }
587 
588         this.setInitialized(true);
589     }
590 
591     /**
592      * Terminates the instance
593      */
594     protected void shutdown()
595     {
596         if( this.isInitialized() == false )
597         {
598             return;
599         }
600 
601         this.getLogger().debug( "Terminating " + this.getClass().getName() );
602 
603         try
604         {
605             // wait for the shutdown thread
606 
607             if( this.shutdownThread != null )
608             {
609                 try
610                 {
611                     this.getLogger().debug( "Waiting for shutdown handler thread to terminate" );
612                     this.shutdownThread.join(JOIN_TIME);
613                     this.shutdownThread = null;
614                     this.getLogger().debug( "Shutdown handler thread is terminated" );
615                 }
616                 catch (InterruptedException e)
617                 {
618                     // nothing to do
619                 }
620             }
621 
622             // dispose the service container
623 
624             if( this.getServiceContainer() != null )
625             {
626                 this.getServiceContainer().dispose();
627                 this.container = null;
628             }
629 
630             this.setInitialized(false);
631         }
632 
633         catch (Exception e)
634         {
635             String msg = "Failed to terminate " + this.getClass().getName();
636             this.getLogger().error(msg,e);
637         }
638     }
639 }