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 IOException upon File name resolution error 054 * @throws UnsupportedScriptTypeException if the extension of the file does 055 * not match that of any known script. 056 * @throws FileNotFoundException if composition file is not found 057 */ 058 public ScriptedContainerBuilderFactory(File compositionFile, ClassLoader classLoader, 059 ScriptedBuilderNameResolver scriptedBuilderResolver) throws UnsupportedScriptTypeException, FileNotFoundException { 060 this(new FileReader(fileExists(compositionFile)), scriptedBuilderResolver.getBuilderClassName(compositionFile), 061 classLoader); 062 } 063 064 /** 065 * Creates a ScriptedContainerBuilderFactory with default script builder 066 * resolver 067 * 068 * @param compositionFile File The script file. 069 * @param classLoader ClassLoader for class resolution once we resolve what 070 * the name of the builder should be. 071 * @see ScriptedContainerBuilderFactory#ScriptedContainerBuilderFactory(File, 072 * ClassLoader, ScriptedBuilderNameResolver) 073 */ 074 public ScriptedContainerBuilderFactory(File compositionFile, ClassLoader classLoader) throws IOException { 075 this(compositionFile, classLoader, new ScriptedBuilderNameResolver()); 076 } 077 078 /** 079 * Creates a ScriptedContainerBuilderFactory with default script builder 080 * resolver and context class loader 081 * 082 * @param compositionFile File The script file. 083 * @see ScriptedContainerBuilderFactory#ScriptedContainerBuilderFactory(File, 084 * ClassLoader, ScriptedBuilderNameResolver) 085 */ 086 public ScriptedContainerBuilderFactory(File compositionFile) throws IOException { 087 this(compositionFile, Thread.currentThread().getContextClassLoader()); 088 } 089 090 /** 091 * Creates a ScriptedContainerBuilderFactory with default script builder 092 * resolver and context class loader 093 * 094 * @param compositionURL The script URL. 095 * @throws UnsupportedScriptTypeException if the extension of the file does 096 * not match that of any known script. 097 */ 098 public ScriptedContainerBuilderFactory(URL compositionURL) { 099 this(compositionURL, Thread.currentThread().getContextClassLoader(), new ScriptedBuilderNameResolver()); 100 } 101 102 /** 103 * Creates a ScriptedContainerBuilderFactory 104 * 105 * @param compositionURL The script URL. 106 * @param builderClassResolver ScriptedBuilderNameResolver the resolver for 107 * figuring out file names to container builder class names. 108 * @param classLoader ClassLoader for class resolution once we resolve what 109 * the name of the builder should be.. the specified builder 110 * using the specified classloader. 111 * @throws UnsupportedScriptTypeException if the extension of the file does 112 * not match that of any known script. 113 */ 114 public ScriptedContainerBuilderFactory(URL compositionURL, ClassLoader classLoader, 115 ScriptedBuilderNameResolver builderClassResolver) throws UnsupportedScriptTypeException { 116 this(compositionURL, builderClassResolver.getBuilderClassName(compositionURL), classLoader); 117 } 118 119 /** 120 * Creates a ScriptedContainerBuilderFactory 121 * 122 * @param compositionURL The script URL. 123 * @param builderClassName the class name of the ContainerBuilder to 124 * instantiate 125 * @param classLoader ClassLoader for class resolution once we resolve what 126 * the name of the builder should be.. the specified builder 127 * using the specified classloader. 128 */ 129 public ScriptedContainerBuilderFactory(URL compositionURL, String builderClassName, ClassLoader classLoader) { 130 createContainerBuilder(compositionURL, builderClassName, classLoader); 131 } 132 133 /** 134 * Creates a ScriptedContainerBuilderFactory with context class loader 135 * 136 * @param composition the Reader encoding the script to create the builder 137 * with 138 * @param builderClassName the class name of the ContainerBuilder to 139 * instantiate 140 * @see ScriptedContainerBuilderFactory#ScriptedContainerBuilderFactory(Reader, 141 * String, ClassLoader) 142 */ 143 public ScriptedContainerBuilderFactory(Reader composition, String builderClassName) { 144 this(composition, builderClassName, Thread.currentThread().getContextClassLoader()); 145 } 146 147 /** 148 * Creates a ScriptedContainerBuilderFactory 149 * 150 * @param composition the Reader encoding the script to create the builder 151 * with 152 * @param builderClassName the class name of the ContainerBuilder to 153 * instantiate 154 * @param classLoader the Classloader to use for instantiation 155 */ 156 public ScriptedContainerBuilderFactory(Reader composition, String builderClassName, ClassLoader classLoader) { 157 createContainerBuilder(composition, builderClassName, classLoader); 158 } 159 160 /** 161 * Performs the actual instantiation of the builder. 162 * 163 * @param composition the composition source object - can be either a 164 * Reader, a URL or a File 165 * @param builderClassName the class name of the ContainerBuilder to 166 * instantiate 167 * @param classLoader the Classloader to use for instantiation 168 */ 169 private void createContainerBuilder(Object composition, String builderClassName, ClassLoader classLoader) { 170 DefaultClassLoadingPicoContainer defaultScriptedContainer; 171 { 172 // transient. 173 DefaultPicoContainer factory = new DefaultPicoContainer(); 174 if (composition == null) { 175 throw new NullPointerException("composition can't be null"); 176 } 177 factory.addComponent(composition); 178 179 if (classLoader == null) { 180 // Thread.currentThread().getContextClassLoader() MAY return 181 // null, while Class.getClassLoader() should NEVER return null. 182 // -MR 183 classLoader = getClass().getClassLoader(); 184 } 185 factory.addComponent(classLoader); 186 187 // If we don't specify the classloader here, some of the things that 188 // make up a scripted container may bomb. And we're only talking a 189 // reload within a webapp! -MR 190 defaultScriptedContainer = new DefaultClassLoadingPicoContainer(classLoader, factory); 191 } 192 ClassName className = new ClassName(builderClassName); 193 MutablePicoContainer mutablePicoContainer = defaultScriptedContainer.addComponent(className, className); 194 ComponentAdapter<?> componentAdapter = mutablePicoContainer.getComponentAdapter(className); 195 containerBuilder = (ScriptedContainerBuilder) componentAdapter.getComponentInstance(defaultScriptedContainer, ComponentAdapter.NOTHING.class); 196 } 197 198 private static File fileExists(final File file) throws FileNotFoundException { 199 if (file.exists()) { 200 return file; 201 } 202 throw new FileNotFoundException("File " + file.getAbsolutePath() + " does not exist."); 203 } 204 205 /** 206 * Returns the created container builder instance. 207 * 208 * @return The ScriptedContainerBuilder instance 209 */ 210 public ScriptedContainerBuilder getContainerBuilder() { 211 return containerBuilder; 212 } 213 214 }