001    /*****************************************************************************
002     * Copyright (C) PicoContainer 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.picocontainer.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.NativeJavaObject;
021    import org.mozilla.javascript.RhinoException;
022    import org.mozilla.javascript.Scriptable;
023    import org.picocontainer.PicoContainer;
024    import org.picocontainer.script.LifecycleMode;
025    import org.picocontainer.script.ScriptedPicoContainerMarkupException;
026    import org.picocontainer.script.ScriptedContainerBuilder;
027    
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            this(script,classLoader, LifecycleMode.AUTO_LIFECYCLE);
045        }
046            
047        public JavascriptContainerBuilder(Reader script, ClassLoader classLoader, LifecycleMode lifecycleMode) {
048            super(script, classLoader, lifecycleMode);
049        }
050    
051        public JavascriptContainerBuilder(URL script, ClassLoader classLoader) {
052            this(script,classLoader, LifecycleMode.AUTO_LIFECYCLE);
053        }
054        
055        public JavascriptContainerBuilder(URL script, ClassLoader classLoader, LifecycleMode lifecycleMode) {
056            super(script, classLoader, lifecycleMode);
057        }
058    
059        protected PicoContainer createContainerFromScript(PicoContainer parentContainer, Object assemblyScope) {
060            final ClassLoader loader = getClassLoader();
061            Context cx = new Context() {
062                public GeneratedClassLoader createClassLoader(ClassLoader parent) {
063                    return new DefiningClassLoader(loader);
064                }
065            };
066            cx = Context.enter(cx);
067    
068            try {
069                Scriptable scope = new ImporterTopLevel(cx);
070                scope.put("parent", scope, parentContainer);
071                scope.put("assemblyScope", scope, assemblyScope);
072                cx.evaluateReader(scope, getScriptReader(), "picocontainer.js", 1, null);
073                Object pico = scope.get("pico", scope);
074    
075                if (pico == null) {
076                    throw new ScriptedPicoContainerMarkupException("The script must define a variable named 'pico'");
077                }
078                if (!(pico instanceof NativeJavaObject)) {
079                    throw new ScriptedPicoContainerMarkupException("The 'pico' variable must be of type " + NativeJavaObject.class.getName());
080                }
081                Object javaObject = ((NativeJavaObject) pico).unwrap();
082                if (!(javaObject instanceof PicoContainer)) {
083                    throw new ScriptedPicoContainerMarkupException("The 'pico' variable must be of type " + PicoContainer.class.getName());
084                }
085                return (PicoContainer) javaObject;
086            } catch (ScriptedPicoContainerMarkupException e) {
087                throw e;
088            } catch (RhinoException e) {
089                            StringBuilder message = new StringBuilder();
090                            message.append("There was an error in script '");
091                            message.append(e.sourceName());
092                            message.append("'.  Line number: ");
093                            message.append(e.lineNumber());
094                            message.append(" and Column number: ");
095                            message.append(e.columnNumber());
096                            message.append(" .");
097                            throw new ScriptedPicoContainerMarkupException(message.toString(), e);
098            } catch (IOException e) {
099                throw new ScriptedPicoContainerMarkupException("IOException encountered, message -'" + e.getMessage() + "'", e);
100            } finally {
101                Context.exit();
102            }
103        }
104    }