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     * Original code by                                                          *
009     *****************************************************************************/
010    package org.nanocontainer.script.rhino;
011    
012    import java.io.IOException;
013    import java.io.Reader;
014    import java.net.URL;
015    
016    import org.mozilla.javascript.Context;
017    import org.mozilla.javascript.DefiningClassLoader;
018    import org.mozilla.javascript.GeneratedClassLoader;
019    import org.mozilla.javascript.ImporterTopLevel;
020    import org.mozilla.javascript.JavaScriptException;
021    import org.mozilla.javascript.NativeJavaObject;
022    import org.mozilla.javascript.NativeJavaPackage;
023    import org.mozilla.javascript.Script;
024    import org.mozilla.javascript.Scriptable;
025    import org.nanocontainer.script.NanoContainerMarkupException;
026    import org.nanocontainer.script.ScriptedContainerBuilder;
027    import org.picocontainer.PicoContainer;
028    
029    /**
030     * {@inheritDoc}
031     * The script has to assign a "pico" variable with an instance of
032     * {@link PicoContainer}.
033     * There is an implicit variable named "parent" that may contain a reference to a parent
034     * container. It is recommended to use this as a constructor argument to the instantiated
035     * PicoContainer.
036     *
037     * @author Paul Hammant
038     * @author Aslak Hellesøy
039     * @author Mauro Talevi
040     */
041    public class JavascriptContainerBuilder extends ScriptedContainerBuilder {
042    
043        public JavascriptContainerBuilder(Reader script, ClassLoader classLoader) {
044            super(script, classLoader);
045        }
046    
047        public JavascriptContainerBuilder(URL script, ClassLoader classLoader) {
048            super(script, classLoader);
049        }
050    
051        protected PicoContainer createContainerFromScript(PicoContainer parentContainer, Object assemblyScope) {
052            final ClassLoader loader = getClassLoader();
053            Context cx = new Context() {
054                public GeneratedClassLoader createClassLoader(ClassLoader parent) {
055                    return new DefiningClassLoader(loader);
056                }
057            };
058            cx = Context.enter(cx);
059    
060            try {
061                ImporterTopLevel imp = new ImporterTopLevel(cx);
062                Scriptable scope = imp;
063                scope.put("parent", scope, parentContainer);
064                scope.put("assemblyScope", scope, assemblyScope);
065                imp.importPackage(cx,
066                        scope, new NativeJavaPackage[]{
067                            new NativeJavaPackage("org.picocontainer.defaults", loader),
068                            new NativeJavaPackage("org.nanocontainer", loader),
069                            new NativeJavaPackage("org.nanocontainer.reflection", loader),
070                            // File, URL and URLClassLoader will be frequently used by scripts.
071                            new NativeJavaPackage("java.net", loader),
072                            new NativeJavaPackage("java.io", loader),
073                        },
074                        null);
075                Script scriptObject = cx.compileReader(scope, getScriptReader(), "javascript", 1, null);
076                scriptObject.exec(cx, scope);
077                Object pico = scope.get("pico", scope);
078    
079                if (pico == null) {
080                    throw new NanoContainerMarkupException("The script must define a variable named 'pico'");
081                }
082                if (!(pico instanceof NativeJavaObject)) {
083                    throw new NanoContainerMarkupException("The 'pico' variable must be of type " + NativeJavaObject.class.getName());
084                }
085                Object javaObject = ((NativeJavaObject) pico).unwrap();
086                if (!(javaObject instanceof PicoContainer)) {
087                    throw new NanoContainerMarkupException("The 'pico' variable must be of type " + PicoContainer.class.getName());
088                }
089                return (PicoContainer) javaObject;
090            } catch (NanoContainerMarkupException e) {
091                throw e;
092            } catch (JavaScriptException e) {
093                Object value = e.getValue();
094                if (value instanceof Throwable) {
095                    throw new NanoContainerMarkupException((Throwable) value);
096                } else {
097                    throw new NanoContainerMarkupException(e);
098                }
099            } catch (IOException e) {
100                throw new NanoContainerMarkupException("IOException encountered, message -'" + e.getMessage() + "'", e);
101            } finally {
102                Context.exit();
103            }
104        }
105    }