001 /******************************************************************************* 002 * Copyright (C) PicoContainer Organization. All rights reserved. 003 * --------------------------------------------------------------------------- 004 * The software in this package is published under the terms of the BSD style 005 * license a copy of which has been included with this distribution in the 006 * LICENSE.txt file. 007 ******************************************************************************/ 008 package org.picocontainer.script; 009 010 import java.io.File; 011 import java.io.FileNotFoundException; 012 import java.io.FileReader; 013 import java.io.IOException; 014 import java.io.Reader; 015 import java.net.URL; 016 import org.picocontainer.ComponentAdapter; 017 import org.picocontainer.DefaultPicoContainer; 018 import org.picocontainer.MutablePicoContainer; 019 import org.picocontainer.classname.DefaultClassLoadingPicoContainer; 020 import org.picocontainer.classname.ClassName; 021 022 /** 023 * Factory class for scripted container builders of various scripting languages. 024 * When using the constructors taking a File, the extensions must be one of the 025 * following: 026 * <ul> 027 * <li>.groovy - Groovy scripts</li> 028 * <li>.bsh - BeanShell scripts</li> 029 * <li>.js - Rhino scripts (Javascript)</li> 030 * <li>.py - Python scripts </li> 031 * <li>.xml - XML scripts</li> 032 * </ul> 033 * with the content of the file of the corresponding scripting language. 034 * 035 * @author Paul Hammant 036 * @author Aslak Helles&oslah;y 037 * @author Obie Fernandez 038 * @author Michael Rimov 039 * @author Mauro Talevi 040 */ 041 public class ScriptedContainerBuilderFactory { 042 043 private ScriptedContainerBuilder containerBuilder; 044 045 /** 046 * Creates a ScriptedContainerBuilderFactory 047 * 048 * @param compositionFile File The script file. 049 * @param classLoader ClassLoader for class resolution once we resolve what 050 * the name of the builder should be. 051 * @param scriptedBuilderResolver ScriptedBuilderNameResolver the resolver of 052 * container builder class names from file names. 053 * @throws UnsupportedScriptTypeException if the extension of the file does 054 * not match that of any known script. 055 * @throws FileNotFoundException if composition file is not found 056 */ 057 public ScriptedContainerBuilderFactory(File compositionFile, ClassLoader classLoader, 058 ScriptedBuilderNameResolver scriptedBuilderResolver) throws UnsupportedScriptTypeException, FileNotFoundException { 059 this(new FileReader(fileExists(compositionFile)), scriptedBuilderResolver.getBuilderClassName(compositionFile), 060 classLoader); 061 } 062 063 /** 064 * Creates a ScriptedContainerBuilderFactory with default script builder 065 * resolver 066 * 067 * @param compositionFile File The script file. 068 * @param classLoader ClassLoader for class resolution once we resolve what 069 * the name of the builder should be. 070 * @see ScriptedContainerBuilderFactory#ScriptedContainerBuilderFactory(File, 071 * ClassLoader, ScriptedBuilderNameResolver) 072 */ 073 public ScriptedContainerBuilderFactory(File compositionFile, ClassLoader classLoader) throws IOException { 074 this(compositionFile, classLoader, new ScriptedBuilderNameResolver()); 075 } 076 077 /** 078 * Creates a ScriptedContainerBuilderFactory with default script builder 079 * resolver and context class loader 080 * 081 * @param compositionFile File The script file. 082 * @see ScriptedContainerBuilderFactory#ScriptedContainerBuilderFactory(File, 083 * ClassLoader, ScriptedBuilderNameResolver) 084 */ 085 public ScriptedContainerBuilderFactory(File compositionFile) throws IOException { 086 this(compositionFile, Thread.currentThread().getContextClassLoader()); 087 } 088 089 /** 090 * Creates a ScriptedContainerBuilderFactory with default script builder 091 * resolver and context class loader 092 * 093 * @param compositionURL The script URL. 094 * @throws UnsupportedScriptTypeException if the extension of the file does 095 * not match that of any known script. 096 */ 097 public ScriptedContainerBuilderFactory(URL compositionURL) { 098 this(compositionURL, Thread.currentThread().getContextClassLoader(), new ScriptedBuilderNameResolver()); 099 } 100 101 /** 102 * Creates a ScriptedContainerBuilderFactory 103 * 104 * @param compositionURL The script URL. 105 * @param builderClassResolver ScriptedBuilderNameResolver the resolver for 106 * figuring out file names to container builder class names. 107 * @param classLoader ClassLoader for class resolution once we resolve what 108 * the name of the builder should be.. the specified builder 109 * using the specified classloader. 110 * @throws UnsupportedScriptTypeException if the extension of the file does 111 * not match that of any known script. 112 */ 113 public ScriptedContainerBuilderFactory(URL compositionURL, ClassLoader classLoader, 114 ScriptedBuilderNameResolver builderClassResolver) throws UnsupportedScriptTypeException { 115 this(compositionURL, builderClassResolver.getBuilderClassName(compositionURL), classLoader); 116 } 117 118 /** 119 * Creates a ScriptedContainerBuilderFactory 120 * 121 * @param compositionURL The script URL. 122 * @param builderClassName the class name of the ContainerBuilder to 123 * instantiate 124 * @param classLoader ClassLoader for class resolution once we resolve what 125 * the name of the builder should be.. the specified builder 126 * using the specified classloader. 127 */ 128 public ScriptedContainerBuilderFactory(URL compositionURL, String builderClassName, ClassLoader classLoader) { 129 createContainerBuilder(compositionURL, builderClassName, classLoader); 130 } 131 132 /** 133 * Creates a ScriptedContainerBuilderFactory with context class loader 134 * 135 * @param composition the Reader encoding the script to create the builder 136 * with 137 * @param builderClassName the class name of the ContainerBuilder to 138 * instantiate 139 * @see ScriptedContainerBuilderFactory#ScriptedContainerBuilderFactory(Reader, 140 * String, ClassLoader) 141 */ 142 public ScriptedContainerBuilderFactory(Reader composition, String builderClassName) { 143 this(composition, builderClassName, Thread.currentThread().getContextClassLoader()); 144 } 145 146 /** 147 * Creates a ScriptedContainerBuilderFactory 148 * 149 * @param composition the Reader encoding the script to create the builder 150 * with 151 * @param builderClassName the class name of the ContainerBuilder to 152 * instantiate 153 * @param classLoader the Classloader to use for instantiation 154 */ 155 public ScriptedContainerBuilderFactory(Reader composition, String builderClassName, ClassLoader classLoader) { 156 createContainerBuilder(composition, builderClassName, classLoader); 157 } 158 159 /** 160 * Performs the actual instantiation of the builder. 161 * 162 * @param composition the composition source object - can be either a 163 * Reader, a URL or a File 164 * @param builderClassName the class name of the ContainerBuilder to 165 * instantiate 166 * @param classLoader the Classloader to use for instantiation 167 */ 168 private void createContainerBuilder(Object composition, String builderClassName, ClassLoader classLoader) { 169 DefaultClassLoadingPicoContainer defaultScriptedContainer; 170 { 171 // transient. 172 DefaultPicoContainer factory = new DefaultPicoContainer(); 173 if (composition == null) { 174 throw new NullPointerException("composition can't be null"); 175 } 176 factory.addComponent(composition); 177 178 if (classLoader == null) { 179 // Thread.currentThread().getContextClassLoader() MAY return 180 // null, while Class.getClassLoader() should NEVER return null. 181 // -MR 182 classLoader = getClass().getClassLoader(); 183 } 184 factory.addComponent(classLoader); 185 186 // If we don't specify the classloader here, some of the things that 187 // make up a scripted container may bomb. And we're only talking a 188 // reload within a webapp! -MR 189 defaultScriptedContainer = new DefaultClassLoadingPicoContainer(classLoader, factory); 190 } 191 ClassName className = new ClassName(builderClassName); 192 MutablePicoContainer mutablePicoContainer = defaultScriptedContainer.addComponent(className, className); 193 ComponentAdapter<?> componentAdapter = mutablePicoContainer.getComponentAdapter(className); 194 containerBuilder = (ScriptedContainerBuilder) componentAdapter.getComponentInstance(defaultScriptedContainer, ComponentAdapter.NOTHING.class); 195 } 196 197 private static File fileExists(final File file) throws FileNotFoundException { 198 if (file.exists()) { 199 return file; 200 } 201 throw new FileNotFoundException("File " + file.getAbsolutePath() + " does not exist."); 202 } 203 204 /** 205 * Returns the created container builder instance. 206 * 207 * @return The ScriptedContainerBuilder instance 208 */ 209 public ScriptedContainerBuilder getContainerBuilder() { 210 return containerBuilder; 211 } 212 213 }