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    }