    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&oslash;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                Scriptable scope = new ImporterTopLevel(cx);
    062                scope.put("parent", scope, parentContainer);
    063                scope.put("assemblyScope", scope, assemblyScope);
    064                ImporterTopLevel.importPackage(cx,
    065                        scope, new NativeJavaPackage[]{
    066                            new NativeJavaPackage("org.picocontainer.defaults", loader),
    067                            new NativeJavaPackage("org.nanocontainer", loader),
    068                            new NativeJavaPackage("org.nanocontainer.reflection", loader),
    069                            // File, URL and URLClassLoader will be frequently used by scripts.
    070                            new NativeJavaPackage("java.net", loader),
    071                            new NativeJavaPackage("java.io", loader),
    072                        },
    073                        null);
    074                Script scriptObject = cx.compileReader(scope, getScriptReader(), "javascript", 1, null);
    075                scriptObject.exec(cx, scope);
    076                Object pico = scope.get("pico", scope);
    077
    078                if (pico == null) {
    079                    throw new NanoContainerMarkupException("The script must define a variable named 'pico'");
    080                }
    081                if (!(pico instanceof NativeJavaObject)) {
    082                    throw new NanoContainerMarkupException("The 'pico' variable must be of type " + NativeJavaObject.class.getName());
    083                }
    084                Object javaObject = ((NativeJavaObject) pico).unwrap();
    085                if (!(javaObject instanceof PicoContainer)) {
    086                    throw new NanoContainerMarkupException("The 'pico' variable must be of type " + PicoContainer.class.getName());
    087                }
    088                return (PicoContainer) javaObject;
    089            } catch (NanoContainerMarkupException e) {
    090                throw e;
    091            } catch (JavaScriptException e) {
    092                Object value = e.getValue();
    093                if (value instanceof Throwable) {
    094                    throw new NanoContainerMarkupException((Throwable) value);
    095                } else {
    096                    throw new NanoContainerMarkupException(e);
    097                }
    098            } catch (IOException e) {
    099                throw new NanoContainerMarkupException("IOException encountered, message -'" + e.getMessage() + "'", e);
    100            } finally {
    101                Context.exit();
    102            }
    103        }
    104    }