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 }