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