001    /*****************************************************************************
002     * Copyright (C) NanoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     *****************************************************************************/
009    
010    package org.nanocontainer.script;
011    
012    import java.io.File;
013    import java.io.FileNotFoundException;
014    import java.io.FileReader;
015    import java.io.IOException;
016    import java.io.Reader;
017    import java.net.URL;
018    import org.nanocontainer.DefaultNanoContainer;
019    import org.picocontainer.ComponentAdapter;
020    import org.picocontainer.defaults.DefaultPicoContainer;
021    
022    /**
023     * The main class for configuration of PicoContainer with various scripting languages.
024     * When using the constructors taking a file, the extensions must be one of the following:
025     * <ul>
026     * <li>.groovy</li>
027     * <li>.bsh</li>
028     * <li>.js</li>
029     * <li>.py</li>
030     * <li>.xml</li>
031     * </ul>
032     * -And the content of the file likewise. See <a href="http://docs.codehaus.org/display/NANO/NanoContainer">NanoContainer documentation</a>
033     * for details.
034     *
035     * @author Paul Hammant
036     * @author Aslak Helles&oslah;y
037     * @author Obie Fernandez
038     * @author Michael Rimov
039     */
040    public class ScriptedContainerBuilderFactory {
041    
042        /**
043         * @deprecated Since NanoContainer RC-2. (14-Dec-2005).  Use ScriptBuilderResolver.GROOVY
044         * instead.
045         */
046        public static final String GROOVY = ".groovy";
047    
048        /**
049         * @deprecated Since NanoContainer RC-2. (14-Dec-2005).  Use ScriptBuilderResolver.BEANSHELL
050         * instead.
051         */
052        public static final String BEANSHELL = ".bsh";
053    
054        /**
055         * @deprecated Since NanoContainer RC-2. (14-Dec-2005).  Use ScriptBuilderResolver.JAVASCRIPT
056         * instead.
057         */
058        public static final String JAVASCRIPT = ".js";
059    
060        /**
061         * @deprecated Since NanoContainer RC-2. (14-Dec-2005).  Use ScriptBuilderResolver.JYTHON
062         * instead.
063         */
064        public static final String JYTHON = ".py";
065    
066        /**
067         * @deprecated Since NanoContainer RC-2. (14-Dec-2005).  Use ScriptBuilderResolver.XML
068         * instead.
069         */
070        public static final String XML = ".xml";
071    
072    
073        private ScriptedContainerBuilder containerBuilder;
074    
075    
076    
077        public ScriptedContainerBuilderFactory(File compositionFile, ClassLoader classLoader) throws IOException, ClassNotFoundException {
078            this(compositionFile, classLoader, new ScriptBuilderResolver());
079        }
080    
081        /**
082         * Added since Nano RC-2.  This allows you to add/modify registered builders to replace
083         * script handling or add new extensions by modifying a constructed ScriptBuilderResolver
084         * before constructing this object.
085         * @param compositionFile File The script file.
086         * @param classLoader ClassLoader for class resolution once we resolve what the name of the
087         * builder should be..
088         * @param builderClassResolver ScriptBuilderResolver the resolver for figuring out
089         * file names to container builder class names.
090         * @throws IOException upon java.io.File name resolution error.
091         * @throws ClassNotFoundException  If there is an error loading
092         * the specified builder using the specified classloader.
093         * @throws UnsupportedScriptTypeException if the extension of the file
094         * does not match that of any known script.
095         */
096        public ScriptedContainerBuilderFactory(File compositionFile, ClassLoader classLoader, ScriptBuilderResolver builderClassResolver) throws IOException, ClassNotFoundException ,UnsupportedScriptTypeException {
097            this(new FileReader(fileExists(compositionFile)), builderClassResolver.getBuilderClassName(compositionFile), classLoader);
098        }
099    
100    
101        public ScriptedContainerBuilderFactory(File compositionFile) throws IOException, ClassNotFoundException {
102            this(compositionFile, Thread.currentThread().getContextClassLoader());
103        }
104    
105        public ScriptedContainerBuilderFactory(URL compositionURL) throws ClassNotFoundException {
106            this(compositionURL, Thread.currentThread().getContextClassLoader(),new ScriptBuilderResolver());
107        }
108    
109        /**
110         *
111         * Added since Nano RC-2.  This allows you to add/modify registered builders to replace
112         * script handling or add new extensions by modifying a constructed ScriptBuilderResolver
113         * before constructing this object.
114         * @param compositionURL The script URL.
115         * @param builderClassResolver ScriptBuilderResolver the resolver for figuring out
116         * file names to container builder class names.
117         * @param classLoader ClassLoader for class resolution once we resolve what the name of the
118         * builder should be..
119         * @throws ClassNotFoundException  If there is an error loading
120         * the specified builder using the specified classloader.
121         * @throws UnsupportedScriptTypeException if the extension of the file
122         * does not match that of any known script.
123         */
124        public ScriptedContainerBuilderFactory(URL compositionURL, ClassLoader classLoader, ScriptBuilderResolver builderClassResolver) throws ClassNotFoundException ,UnsupportedScriptTypeException {
125            this(compositionURL, builderClassResolver.getBuilderClassName(compositionURL), classLoader);
126        }
127    
128    
129        public ScriptedContainerBuilderFactory(URL compositionURL, String builderClassName, ClassLoader contextClassLoader) throws ClassNotFoundException {
130            createContainerBuilder(compositionURL, contextClassLoader, builderClassName);
131        }
132    
133    
134        public ScriptedContainerBuilderFactory(Reader composition, String builderClass) throws ClassNotFoundException {
135            this(composition, builderClass, Thread.currentThread().getContextClassLoader());
136        }
137    
138        /**
139         * Allows you to create a factory that isntantiats the builder class you desire.
140         * @param composition Reader the script you wish to create the builder for.
141         * @param builderClass String the builder class that instantiate.
142         * @param classLoader ClassLoader the classloader to use for instantiation.
143         * @throws ClassNotFoundException if the specified class cannot be found.
144         */
145        public ScriptedContainerBuilderFactory(Reader composition, String builderClass, ClassLoader classLoader) throws ClassNotFoundException {
146            createContainerBuilder(composition, classLoader, builderClass);
147        }
148    
149    
150        /**
151         * Performs the actual instantiation of the builder.
152         * @param composition Object  Either a URL or a File, it doesn't matter which here.
153         * @param classLoader ClassLoader the classloader ot use for classname resolution
154         * and loading.
155         * @param builderClass String the builder class to load.
156         * @throws ClassNotFoundException if the specified builder class cannot be loaded.
157         */
158        private void createContainerBuilder(Object composition, ClassLoader classLoader, String builderClass) throws ClassNotFoundException {
159            DefaultNanoContainer defaultNanoContainer;
160            {
161                // transient.
162                DefaultPicoContainer factory = new DefaultPicoContainer();
163                if(composition == null) {
164                    throw new NullPointerException("composition can't be null");
165                }
166                factory.registerComponentInstance(composition);
167    
168                if(classLoader == null) {
169                    // on some weird JVMs (like jeode) Thread.currentThread().getContextClassLoader() returns null !?!?
170                    //Found out on JDK 1.5 javadocs that Thread.currentThread().getContextClassLoader() MAY return null
171                    //while Class.getClassLoader() should NEVER return null.  -MR
172                    //
173                    //
174                    classLoader = getClass().getClassLoader();
175                }
176                factory.registerComponentInstance(classLoader);
177    
178                //
179                //If we don't specify the classloader here, some of the things that make
180                //up a nanocontainer may bomb. And we're only talking a reload
181                //within a webapp!  -MR
182                //
183                defaultNanoContainer = new DefaultNanoContainer(classLoader,factory);
184            }
185            ComponentAdapter componentAdapter = defaultNanoContainer.registerComponentImplementation(builderClass);
186            containerBuilder = (ScriptedContainerBuilder) componentAdapter.getComponentInstance(defaultNanoContainer.getPico());
187        }
188    
189        private static File fileExists(final File file) throws FileNotFoundException {
190            if (file.exists()) {
191                return file;
192            } else {
193                //todo a proper exception.
194                throw new FileNotFoundException("File " + file.getAbsolutePath() + " does not exist.");
195            }
196        }
197    
198        /**
199         * This function does not support custom file type resolving -- for backwards
200         * compatibility, it uses a fresh instance of ScriptBuilderResolver for
201         * each invocation.  Use ScriptBuilderResolver instead.
202         * @param extension String the file extension to res
203         * @return String the classname to use for the specified extension.
204         * @deprecated Since NanoContainer 1.0 RC-2.  Use the class ScriptBuilderResolver
205         * for this functionality.
206         */
207        public static String getBuilderClassName(final String extension) {
208            return new ScriptBuilderResolver().getBuilderClassName(extension);
209        }
210    
211    
212        /**
213         * Retrieve the created container builder instance.
214         * @return ScriptedContainerBuilder instance, never null.
215         */
216        public ScriptedContainerBuilder getContainerBuilder() {
217            return containerBuilder;
218        }
219    
220    }