    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    }