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 }