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.kernel;
018
019 import java.io.BufferedReader;
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.io.InputStreamReader;
023 import java.util.HashMap;
024 import java.util.Map;
025
026 import java.util.concurrent.ConcurrentHashMap;
027 import org.apache.xbean.kernel.standard.StandardKernelFactory;
028
029 /**
030 * The Kernel factory is used to construct and locate Kernels. This class is loosly based on the SAXParserFactory and
031 * the JMX MBeanServerFactory. To constuct a kernel use the following:
032 * <p><blockquote><pre>
033 * Kernel kernel = KernelFactory.newInstance().createKernel(name);
034 * </pre></blockquote>
035 *
036 * @org.apache.xbean.XBean namespace="http://xbean.apache.org/schemas/kernel" element="load-all-main"
037 * description="Creates kernels"
038 *
039 * @author Dain Sundstrom
040 * @version $Id$
041 * @since 2.0
042 */
043 public abstract class KernelFactory {
044 /**
045 * The name of the system property and META-INF/services used to locate the kernel factory class.
046 */
047 public static final String KERNEL_FACTORY_KEY = KernelFactory.class.getName();
048
049 private static final ConcurrentHashMap kernels = new ConcurrentHashMap(1);
050
051 /**
052 * Gets the kernel registered under the specified name. If no kernel is registered with the specified name, null
053 * is returned.
054 *
055 * @param name the name of the kernel to return
056 * @return the kernel or null if no kernel is registered under the specified name
057 */
058 public static Kernel getKernel(String name) {
059 if (name == null) throw new NullPointerException("name is null");
060 return (Kernel) kernels.get(name);
061 }
062
063 /**
064 * Gets a map of the existing kernels by kernel name.
065 *
066 * @return the existing kernels by kernel name.
067 */
068 public static Map getKernels() {
069 return new HashMap(kernels);
070 }
071
072 /**
073 * Creates a kernel with the specified name. This method will attempt to locate a KernelFactory implementation
074 * using the following procedure
075 * <ul> <li>
076 * The org.apache.xbean.kernel.KernelFactory system property.
077 * </li> <li>
078 * Use the <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html">Jar Service Specification</a>
079 * This method will attempt to get the factory name from the file
080 * META-INF/services/org.apache.xbean.kernel.KernelFactory loaded using the thread context class loader.
081 * </li>
082 * <li>
083 * The StandardKernel implementation.
084 * </li>
085 * </ul>
086 * The factory class is loaded and constucted using the thread context class loader, if present, or the class
087 * loader of this class.
088 *
089 * @return the kernel factory implementation
090 * @throws KernelFactoryError if the specified kernel factory can not be created
091 */
092 public static KernelFactory newInstance() throws KernelFactoryError {
093 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
094 if (classLoader == null) {
095 classLoader = KernelFactory.class.getClassLoader();
096 }
097
098 // System property
099 try {
100 String kernelFactoryName = System.getProperty(KERNEL_FACTORY_KEY);
101 if (kernelFactoryName != null) {
102 return createKernelFactory(kernelFactoryName, classLoader);
103 }
104 } catch (SecurityException se) {
105 }
106
107 // Jar Service Specification - http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html
108 String serviceId = "META-INF/services/" + KERNEL_FACTORY_KEY;
109 InputStream inputStream = null;
110 try {
111 inputStream = classLoader.getResourceAsStream(serviceId);
112 if (inputStream != null) {
113 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
114 String kernelFactoryName = reader.readLine();
115 reader.close();
116
117 if (kernelFactoryName != null && kernelFactoryName.length() > 0) {
118 return createKernelFactory(kernelFactoryName, classLoader);
119 }
120 }
121 } catch (Exception ignored) {
122 } finally {
123 if (inputStream != null) {
124 try {
125 inputStream.close();
126 } catch (IOException ignored) {
127 }
128 inputStream = null;
129 }
130 }
131
132 // Default is the standard kernel
133 return new StandardKernelFactory();
134 }
135
136 /**
137 * Removes the kernel instance from the internal kernel registry. This method should only be called by the kernel
138 * instance itself during destruction.
139 * @param kernel the kernel to destroy
140 * @throws KernelFactoryError if the kernel is still running
141 */
142 public static void destroyInstance(Kernel kernel) throws KernelFactoryError {
143 if (kernel.isRunning()) {
144 throw new KernelFactoryError("Kernel is running: name" + kernel.getKernelName());
145 }
146
147 kernels.remove(kernel.getKernelName(), kernel);
148 }
149
150 private static KernelFactory createKernelFactory(String className, ClassLoader classLoader) throws KernelFactoryError {
151 try {
152 return (KernelFactory) classLoader.loadClass(className).newInstance();
153 } catch (ClassCastException e) {
154 throw new KernelFactoryError("Kernel factory class does not implement KernelFactory: " + className);
155 } catch (ClassNotFoundException e) {
156 throw new KernelFactoryError("Kernel factory class not found: " + className);
157 } catch (Exception e) {
158 throw new KernelFactoryError("Unable to instantiate kernel factory class: " + className, e);
159 }
160 }
161
162 /**
163 * Creates a new kernel instance and registers it with the static KernelFactory registry. This allows the kernel
164 * to be retrieved from the {@link KernelFactory#getKernel(String)} method.
165 *
166 * @param name the name of the kernel to create
167 * @return the new kernel
168 * @throws KernelAlreadyExistsException is a kernel already exists with the specified name
169 */
170 public final Kernel createKernel(String name) throws KernelAlreadyExistsException {
171 if (name == null) throw new NullPointerException("name is null");
172
173 // quick check to see if a kernel already registerd wit the name
174 if (kernels.containsKey(name)) {
175 throw new KernelAlreadyExistsException(name);
176 }
177
178 // create the kernel -- this may be an unnecessary construction, but it shouldn't be a big deal
179 Kernel kernel = createKernelInternal(name);
180
181 // register the kernel, checking if someone snuck in an registered a kernel while we were creating ours
182 if (kernels.putIfAbsent(name, kernel) != null) {
183 throw new KernelAlreadyExistsException(name);
184 }
185
186 return kernel;
187 }
188
189 /**
190 * Creates the actual kernel instance which will be registerd in the KernelFactory.
191 *
192 * @param name the kernel name
193 * @return a new kernel instance
194 */
195 protected abstract Kernel createKernelInternal(String name);
196 }