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 }