001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.system.main;
019    
020    import org.apache.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.apache.geronimo.common.GeronimoEnvironment;
023    import org.apache.geronimo.gbean.AbstractName;
024    import org.apache.geronimo.gbean.AbstractNameQuery;
025    import org.apache.geronimo.kernel.Kernel;
026    import org.apache.geronimo.kernel.KernelFactory;
027    import org.apache.geronimo.kernel.config.ConfigurationManager;
028    import org.apache.geronimo.kernel.config.ConfigurationUtil;
029    import org.apache.geronimo.kernel.config.PersistentConfigurationList;
030    import org.apache.geronimo.kernel.log.GeronimoLogging;
031    import org.apache.geronimo.kernel.repository.Artifact;
032    import org.apache.geronimo.system.serverinfo.DirectoryUtils;
033    
034    import java.io.File;
035    import java.io.IOException;
036    import java.io.InputStream;
037    import java.io.PrintStream;
038    import java.util.ArrayList;
039    import java.util.Iterator;
040    import java.util.List;
041    import java.util.Set;
042    
043    /**
044     * @version $Rev:385659 $ $Date: 2006-12-14 03:10:31 -0800 (Thu, 14 Dec 2006) $
045     */
046    public class Daemon {
047        private final static String ARGUMENT_NO_PROGRESS = "--quiet";
048        private final static String ARGUMENT_LONG_PROGRESS = "--long";
049        private final static String ARGUMENT_VERBOSE_SHORTFORM = "-v";
050        private final static String ARGUMENT_VERBOSE = "--verbose";
051        private final static String ARGUMENT_MORE_VERBOSE_SHORTFORM = "-vv";
052        private final static String ARGUMENT_MORE_VERBOSE = "--veryverbose";
053        private final static String ARGUMENT_MODULE_OVERRIDE = "--override";
054        private static boolean started = false;
055        private static Log log;
056        private StartupMonitor monitor;
057        private List configs = new ArrayList();
058        private String verboseArg = null;
059        private String noProgressArg = null;
060        private String longProgressArg = null;
061    
062        private Daemon(String[] args) {
063            // Very first startup tasks
064            long start = System.currentTimeMillis();
065            // Command line arguments affect logging configuration, etc.
066            if(processArguments(args)) {
067                System.out.println("Booting Geronimo Kernel (in Java " + System.getProperty("java.version") + ")...");
068                System.out.flush();
069    
070                // Initialization tasks that must run before anything else
071                initializeSystem();
072    
073                monitor.systemStarting(start);
074                doStartup();
075            } else {
076                System.exit(1);
077                throw new AssertionError();
078            }
079        }
080    
081        private void printHelp(PrintStream out) {
082            out.println();
083            out.println("Syntax: java -jar bin/server.jar [options]");
084            out.println();
085            out.println("Available options are: ");
086            out.println("  "+ARGUMENT_NO_PROGRESS);
087            out.println("             Suppress the normal startup progress bar.  This is typically\n" +
088                        "             used when redirecting console output to a file, or starting\n" +
089                        "             the server from an IDE or other tool.");
090            out.println("  "+ARGUMENT_LONG_PROGRESS);
091            out.println("             Write startup progress to the console in a format that is\n" +
092                        "             suitable for redirecting console output to a file, or starting\n" +
093                        "             the server from an IDE or other tool (doesn't use linefeeds to\n" +
094                        "             update the progress information that is used by default if you\n" +
095                        "             don't specify " +ARGUMENT_NO_PROGRESS +" or "+ARGUMENT_LONG_PROGRESS+").\n");
096            out.println("  "+ARGUMENT_VERBOSE_SHORTFORM +" " +ARGUMENT_VERBOSE);
097            out.println("             Reduces the console log level to DEBUG, resulting in more\n" +
098                        "             console output than is normally present.");
099            out.println("  "+ARGUMENT_MORE_VERBOSE_SHORTFORM +" " +ARGUMENT_MORE_VERBOSE);
100            out.println("             Reduces the console log level to TRACE, resulting in still\n" +
101                        "             more console output.");
102            out.println();
103            out.println("  "+ARGUMENT_MODULE_OVERRIDE+" [moduleId] [moduleId] ...");
104            out.println("             USE WITH CAUTION!  Overrides the modules in\n" +
105                        "             var/config/config.xml such that only the modules listed on\n" +
106                        "             the command line will be started.  Note that many J2EE\n" +
107                        "             features depend on certain modules being started, so you\n" +
108                        "             should be very careful what you omit.  Any arguments after\n" +
109                        "             this are assumed to be module names.");
110            out.println();
111            out.println("In addition you may specify a replacement for var/config/config.xml using by setting the property\n" +
112                        "-Dorg.apache.geronimo.config.file=var/config/<my-config.xml>\n" +
113                        "This is resolved relative to the geronimo base directory.");
114            out.println();
115        }
116    
117        /**
118         * @return true if the server startup should proceed (all arguments
119         *              make sense and the user didn't ask for help)
120         */
121        private boolean processArguments(String[] args) {
122            boolean override = false;
123            boolean help = false;
124            for (int i = 0; i < args.length; i++) {
125                if(override) {
126                    configs.add(Artifact.create(args[i]));
127                } else if (args[i].equals(ARGUMENT_NO_PROGRESS)) {
128                    noProgressArg = ARGUMENT_NO_PROGRESS;
129                } else if (args[i].equals(ARGUMENT_LONG_PROGRESS)) {
130                    longProgressArg = ARGUMENT_LONG_PROGRESS;
131                } else if (args[i].equals(ARGUMENT_VERBOSE_SHORTFORM) ||
132                        args[i].equals(ARGUMENT_VERBOSE)) {
133                    if (verboseArg == null) {
134                        verboseArg = ARGUMENT_VERBOSE;
135                    }
136                } else if (args[i].equals(ARGUMENT_MORE_VERBOSE_SHORTFORM) ||
137                        args[i].equals(ARGUMENT_MORE_VERBOSE)) {
138                    if (verboseArg == null) {
139                        verboseArg = ARGUMENT_MORE_VERBOSE;
140                    }
141                } else if (args[i].equals(ARGUMENT_MODULE_OVERRIDE)) {
142                    override = true;
143                } else if(args[i].equalsIgnoreCase("-help") || args[i].equalsIgnoreCase("--help") ||
144                        args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("/?")) {
145                    help = true;
146                } else {
147                    System.out.println("Unrecognized argument: "+args[i]);
148                    help = true;
149                }
150            }
151            if(help) {
152                printHelp(System.out);
153            }
154            return !help;
155        }
156    
157        private void initializeSystem() {
158            if (!started) {
159                started = true;
160    
161                // Perform initialization tasks common with the various Geronimo environments
162                GeronimoEnvironment.init();
163    
164                // This MUST be done before the first log is acquired (WHICH THE STARTUP MONITOR 5 LINES LATER DOES!)
165                // Generally we want to suppress anything but WARN until the log GBean starts up
166                GeronimoLogging.initialize(verboseArg == null || verboseArg.equals(ARGUMENT_VERBOSE) ? GeronimoLogging.WARN : GeronimoLogging.DEBUG);
167                // The following will be used once the log GBean starts up
168                GeronimoLogging.setConsoleLogLevel(verboseArg == null ? GeronimoLogging.INFO : verboseArg.equals(ARGUMENT_VERBOSE) ? GeronimoLogging.DEBUG : GeronimoLogging.TRACE);
169                log = LogFactory.getLog(Daemon.class.getName());
170            }
171    
172            if (verboseArg != null || noProgressArg != null) {
173                monitor = new SilentStartupMonitor();
174            } else {
175                if (longProgressArg != null)
176                    monitor = new LongStartupMonitor();
177                else
178                    monitor = new ProgressBarStartupMonitor();
179            }
180    
181            // JVMCheck();   // Removed for 1.1
182        }
183    
184        private void JVMCheck() {
185            String jvmVersion = System.getProperty("java.specification.version");
186            if (! jvmVersion.equals("1.4"))
187                log.warn("\n====================================== Warning =======================================\n" +
188                         " Geronimo is currently only certified on version 1.4 of the Java Virtual Machine.\n" +
189                         " Use of version " + jvmVersion + " is not currently supported.  Use at your own risk.\n" +
190                         " Check http://geronimo.apache.org for current information on JDK certification level.\n" +
191                         "====================================== Warning =======================================");
192        }
193    
194        private void doStartup() {
195            try {
196                // Check that the tmpdir exists - if not give friendly msg and exit
197                // since we allow it to be configured in geronimo.bat and geronimo.sh
198                // (since 1.0 release) the same way Tomcat allows it to be configured.
199                String tmpDir = System.getProperty("java.io.tmpdir");
200                if (tmpDir == null || (!(new File(tmpDir)).exists()) ||
201                        (!(new File(tmpDir)).isDirectory())) {
202                        System.err.println("The java.io.tmpdir system property specifies the "+
203                                "non-existent directory " +tmpDir);
204                        System.exit(1);
205                        throw new AssertionError();
206                    }
207    
208                // Determine the geronimo installation directory
209                File geronimoInstallDirectory = DirectoryUtils.getGeronimoInstallDirectory();
210                if (geronimoInstallDirectory == null) {
211                    System.err.println("Could not determine geronimo installation directory");
212                    System.exit(1);
213                    throw new AssertionError();
214                }
215    
216                ClassLoader classLoader = Daemon.class.getClassLoader();
217    
218                // create the kernel
219                final Kernel kernel = KernelFactory.newInstance().createKernel("geronimo");
220    
221                // boot the kernel
222                try {
223                    kernel.boot();
224                } catch (Exception e) {
225                    e.printStackTrace();
226                    System.exit(1);
227                    throw new AssertionError();
228                }
229    
230                // add our shutdown hook
231                Runtime.getRuntime().addShutdownHook(new Thread("Geronimo shutdown thread") {
232                    public void run() {
233                        System.out.println("\rServer shutdown begun              ");
234                        kernel.shutdown();
235                        System.out.println("Server shutdown completed");
236                    }
237                });
238    
239                // load this configuration
240                InputStream in = classLoader.getResourceAsStream("META-INF/config.ser");
241                try {
242                    ConfigurationUtil.loadBootstrapConfiguration(kernel, in, classLoader);
243                } finally {
244                    if (in != null) {
245                        try {
246                            in.close();
247                        } catch (IOException ignored) {
248                            // ignored
249                        }
250                    }
251                }
252    
253                monitor.systemStarted(kernel);
254    
255                AbstractNameQuery query = new AbstractNameQuery(PersistentConfigurationList.class.getName());
256    
257                if (configs.isEmpty()) {
258                    // --override wasn't used (nothing explicit), see what was running before
259                    Set configLists = kernel.listGBeans(query);
260                    for (Iterator i = configLists.iterator(); i.hasNext();) {
261                        AbstractName configListName = (AbstractName) i.next();
262                        try {
263                            configs.addAll((List) kernel.invoke(configListName, "restore"));
264                        } catch (IOException e) {
265                            System.err.println("Unable to restore last known configurations");
266                            e.printStackTrace();
267                            kernel.shutdown();
268                            System.exit(1);
269                            throw new AssertionError();
270                        }
271                    }
272                }
273    
274                monitor.foundModules((Artifact[]) configs.toArray(new Artifact[configs.size()]));
275    
276                // load the rest of the configurations
277                try {
278                    ConfigurationManager configurationManager = ConfigurationUtil.getConfigurationManager(kernel);
279                    try {
280                        for (Iterator i = configs.iterator(); i.hasNext();) {
281                            Artifact configID = (Artifact) i.next();
282                            monitor.moduleLoading(configID);
283                            configurationManager.loadConfiguration(configID);
284                            monitor.moduleLoaded(configID);
285                            monitor.moduleStarting(configID);
286                            configurationManager.startConfiguration(configID);
287                            monitor.moduleStarted(configID);
288                        }
289                    } finally {
290                        ConfigurationUtil.releaseConfigurationManager(kernel, configurationManager);
291                    }
292                } catch (Exception e) {
293                    //Exception caught when starting configurations, starting kernel shutdown
294                    monitor.serverStartFailed(e);
295                    try {
296                        kernel.shutdown();
297                    } catch (Exception e1) {
298                        System.err.println("Exception caught during kernel shutdown");
299                        e1.printStackTrace();
300                    }
301                    System.exit(1);
302                    throw new AssertionError();
303                }
304    
305                // Tell every persistent configuration list that the kernel is now fully started
306                Set configLists = kernel.listGBeans(query);
307                for (Iterator i = configLists.iterator(); i.hasNext();) {
308                    AbstractName configListName = (AbstractName) i.next();
309                    kernel.setAttribute(configListName, "kernelFullyStarted", Boolean.TRUE);
310                }
311    
312                // Startup sequence is finished
313                monitor.startupFinished();
314                monitor = null;
315    
316                // capture this thread until the kernel is ready to exit
317                while (kernel.isRunning()) {
318                    try {
319                        synchronized (kernel) {
320                            kernel.wait();
321                        }
322                    } catch (InterruptedException e) {
323                        // continue
324                    }
325                }
326            } catch (Exception e) {
327                if (monitor != null) {
328                    monitor.serverStartFailed(e);
329                }
330                e.printStackTrace();
331                System.exit(1);
332                throw new AssertionError();
333            }
334        }
335    
336        private void AddToSystemProperty(String propertyName, List dirsFromManifest, File geronimoInstallDirectory) {
337            String dirs = System.getProperty(propertyName, "");
338            for (Iterator iterator = dirsFromManifest.iterator(); iterator.hasNext();) {
339                String directoryName = (String) iterator.next();
340                File directory = new File(directoryName);
341                if (!directory.isAbsolute()) {
342                    directory = new File(geronimoInstallDirectory, directoryName);
343                }
344    
345                if (dirs.length() > 0) {
346                    dirs += File.pathSeparatorChar;
347                }
348                dirs += directory.getAbsolutePath();
349            }
350            if (dirs.length() > 0) {
351                System.setProperty(propertyName, dirs);
352            }
353            log.debug(propertyName + "=" + System.getProperty(propertyName));
354        }
355    
356        /**
357         * Static entry point allowing a Kernel to be run from the command line.
358         *
359         * Once the Kernel is booted and the configuration is loaded, the process
360         * will remain running until the shutdown() method on the kernel is
361         * invoked or until the JVM exits.
362         *
363         * @param args the command line arguments
364         */
365        public static void main(String[] args) {
366            new Daemon(args);
367        }
368    
369    }