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    }