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    package org.apache.xbean.server.main;
018    
019    import java.util.Map;
020    import java.util.Collections;
021    import java.util.Iterator;
022    
023    import org.apache.xbean.kernel.Kernel;
024    import org.apache.xbean.kernel.KernelFactory;
025    import org.apache.xbean.kernel.ServiceName;
026    import org.apache.xbean.kernel.StringServiceName;
027    import org.apache.xbean.kernel.StaticServiceFactory;
028    
029    /**
030     * KernelMain is the standard entry point class used for a server.  It will initalize a kernel with a set of services
031     * and can optional hold the thread of execution until the kernel or virtual machine is destroyed.
032     *
033     * @org.apache.xbean.XBean namespace="http://xbean.apache.org/schemas/server" element="kernel-main"
034     *     description="Standard entry point for a kernel based server."
035     *
036     * @author Dain Sundstrom
037     * @version $Id$
038     * @since 2.0
039     */
040    public class KernelMain implements Main {
041        private static final String DEFAULT_KERNEL_NAME = "xbean";
042    
043        private Kernel kernel;
044        private ClassLoader classLoader;
045        private Map services = Collections.EMPTY_MAP;
046        private boolean daemon = true;
047        private Main next;
048    
049        /**
050         * Gets the kernel that will be initialized in the main method.  If the kernel is null, a new kernel will be created
051         * and initialized in the main method.
052         * @return the kernel that will be initialized in the main method
053         */
054        public Kernel getKernel() {
055            return kernel;
056        }
057    
058        /**
059         * Sets the kernel to be initialized in the main method.
060         * @param kernel the kernel to initialize in the main method
061         */
062        public void setKernel(Kernel kernel) {
063            this.kernel = kernel;
064        }
065    
066        /**
067         * Gets the class loader which is used as the thread context class loader during the main method.
068         * @return the class loader which is used as the thread context class loader during the main method
069         */
070        public ClassLoader getClassLoader() {
071            return classLoader;
072        }
073    
074        /**
075         * Sets the class loader to use as the thread context class loader during the main method.
076         * @param classLoader the class loader to use as the thread context class loader during the main method
077         */
078        public void setClassLoader(ClassLoader classLoader) {
079            this.classLoader = classLoader;
080        }
081    
082        /**
083         * Gets the services to be registered with the kernel during the main method.
084         * @return the services to be mounted added to the kernel during the main method
085         */
086        public Map getServices() {
087            return services;
088        }
089    
090        /**
091         * Sets the services to be registered with the kernel during the main method.
092         * @param services the services to be registered with the kernel during the main method
093         */
094        public void setServices(Map services) {
095            this.services = services;
096        }
097    
098        /**
099         * Determines if the main method should hold the thread until the kernel is destroyed.
100         * @return true if the main method should hold the thread until the kernel is destroyed; false otherwise
101         */
102        public boolean isDaemon() {
103            return daemon;
104        }
105    
106        /**
107         * Sets the main method to hold the thread until the kernel is destroyed.
108         * @param daemon true if the main method should hold the thread until the kernel is destroyed
109         */
110        public void setDaemon(boolean daemon) {
111            this.daemon = daemon;
112        }
113    
114        /**
115         * Gets the next main to call after the kernel has been initialized, but before destroying the kernel.
116         * @return the next main to call after the kernel has been initialized
117         */
118        public Main getNext() {
119            return next;
120        }
121    
122        /**
123         * Sets the next main to call after the kernel has been initialized.
124         * @param next the next main to call after the kernel has been initialized
125         */
126        public void setNext(Main next) {
127            this.next = next;
128        }
129    
130        /**
131         * Registers the services with the kernel, calls the next main, optionally holds the thread until the kernel is
132         * destroyed, and then destroys the kernel.
133         * @param args the arguments passed the next main
134         */
135        public void main(String[] args) {
136            if (classLoader == null) {
137                classLoader = Thread.currentThread().getContextClassLoader();
138            }
139    
140            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
141            Thread.currentThread().setContextClassLoader(classLoader);
142            try {
143                // create a default kernel if necessary
144                if (kernel == null) {
145                    kernel = KernelFactory.newInstance().createKernel(DEFAULT_KERNEL_NAME);
146                }
147    
148                boolean failed = false;
149                try {
150                    // bind the bootstrap services
151                    for (Iterator iterator = services.entrySet().iterator(); iterator.hasNext();) {
152                        Map.Entry entry = (Map.Entry) iterator.next();
153                        String name = (String) entry.getKey();
154                        Object service = entry.getValue();
155    
156                        try {
157                            ServiceName serviceName = new StringServiceName(name);
158                            kernel.registerService(serviceName, new StaticServiceFactory(service, classLoader));
159                            kernel.startService(serviceName);
160                        } catch (Exception e) {
161                            throw new FatalStartupError("Unable to bind bootstrap service '" + name + "' into the kernel", e);
162                        }
163                    }
164    
165                    // if we have a child main class call it
166                    if (next != null) {
167                        next.main(args);
168                    }
169    
170                    // if we are a daemon we wait here until the server stops
171                    if (daemon) {
172                        // add our shutdown hook
173                        Runtime.getRuntime().addShutdownHook(new DestroyKernelThread(kernel));
174    
175                        // wait for the kernel to be destroyed
176                        kernel.waitForDestruction();
177                    }
178                } catch (RuntimeException e) {
179                    failed = true;
180                    throw e;
181                } catch (Error e) {
182                    failed = true;
183                    throw e;
184                } finally {
185                    try {
186                        kernel.destroy();
187                    } catch (Exception e) {
188                        // if we are not alredy throwing an exception, throw a new exception
189                        if (!failed) {
190                            throw new FatalStartupError("Exception while shutting down kernel", e);
191                        }
192                    }
193                }
194            } finally {
195                Thread.currentThread().setContextClassLoader(oldClassLoader);
196            }
197        }
198        
199        public void destroy() {
200            if( kernel!=null ) {
201                kernel.destroy();
202            }
203        }
204    
205        private static class DestroyKernelThread extends Thread {
206            private final Kernel kernel;
207    
208            private DestroyKernelThread(Kernel kernel) {
209                super("Destroy Kernel Shutdown Hook");
210                this.kernel = kernel;
211            }
212    
213            public void run() {
214                kernel.destroy();
215            }
216        }
217        
218    }