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 }