    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 Leo Simons                                               *
    009     *****************************************************************************/
    010    package org.nanocontainer.script.bsh;
    011
    012    import bsh.EvalError;
    013    import bsh.Interpreter;
    014    import org.picocontainer.Parameter;
    015    import org.picocontainer.PicoContainer;
    016    import org.picocontainer.PicoInitializationException;
    017    import org.picocontainer.PicoIntrospectionException;
    018    import org.picocontainer.defaults.AbstractComponentAdapter;
    019    import org.picocontainer.defaults.UnsatisfiableDependenciesException;
    020
    021    import java.io.IOException;
    022    import java.io.InputStreamReader;
    023    import java.io.Reader;
    024    import java.net.URL;
    025    import java.util.Arrays;
    026    import java.util.Collections;
    027
    028    /**
    029     * This adapter relies on <a href="http://beanshell.org/">Bsh</a> for instantiation
    030     * (and possibly also initialisation) of component instances.
    031     * <p/>
    032     * When {@link org.picocontainer.ComponentAdapter#getComponentInstance} is called (by PicoContainer),
    033     * the adapter instance will look for a script with the same name as the component implementation
    034     * class (but with the .bsh extension). This script must reside in the same folder as the class.
    035     * (It's ok to have them both in a jar).
    036     * <p/>
    037     * The bsh script's only contract is that it will have to instantiate a bsh variable called
    038     * "instance".
    039     * <p/>
    040     * The script will have access to the following variables:
    041     * <ul>
    042     * <li>adapter - the adapter calling the script</li>
    043     * <li>picoContainer - the MutablePicoContainer calling the adapter</li>
    044     * <li>componentKey - the component key</li>
    045     * <li>componentImplementation - the component implementation</li>
    046     * <li>parameters - the ComponentParameters (as a List)</li>
    047     * </ul>
    048     * @author <a href="mail at leosimons dot com">Leo Simons</a>
    049     * @author Aslak Hellesoy
    050     * @version $Id: BeanShellComponentAdapter.java 3144 2006-12-26 10:12:19Z mauro $
    051     */
    052    public class BeanShellComponentAdapter extends AbstractComponentAdapter {
    053        private final Parameter[] parameters;
    054
    055        private Object instance = null;
    056
    057        /**
    058         * Classloader to set for the BeanShell interpreter.
    059         */
    060        private final ClassLoader classLoader;
    061
    062        public BeanShellComponentAdapter(final Object componentKey, final Class componentImplementation, final Parameter[] parameters, final ClassLoader classLoader) {
    063            super(componentKey, componentImplementation);
    064            this.parameters = parameters;
    065            this.classLoader = classLoader;
    066        }
    067
    068        public BeanShellComponentAdapter(final Object componentKey, final Class componentImplementation, final Parameter[] parameters) {
    069            this(componentKey, componentImplementation, parameters, BeanShellComponentAdapter.class.getClassLoader());
    070        }
    071
    072        public Object getComponentInstance(PicoContainer pico)
    073                throws PicoInitializationException, PicoIntrospectionException {
    074
    075            if (instance == null) {
    076                try {
    077                    Interpreter i = new Interpreter();
    078                    i.setClassLoader(classLoader);
    079                    i.set("adapter", this);
    080                    i.set("picoContainer", pico);
    081                    i.set("componentKey", getComponentKey());
    082                    i.set("componentImplementation", getComponentImplementation());
    083                    i.set("parameters", parameters != null ? Arrays.asList(parameters) : Collections.EMPTY_LIST);
    084                    i.eval("import " + getComponentImplementation().getName() + ";");
    085
    086                    String scriptPath = "/" + getComponentImplementation().getName().replace('.', '/') + ".bsh";
    087
    088                    // Inside IDEA, this relies on the compilation output path being the same directory as the source path.
    089                    // kludge - copy ScriptableDemoBean.bsh to the same location in the test output compile class path.
    090                    // the same problem exists for maven, but some custom jelly script will be able to fix that.
    091                    URL scriptURL = getComponentImplementation().getResource(scriptPath);
    092                    if (scriptURL == null) {
    093                        throw new BeanShellScriptInitializationException("Couldn't load script at path " + scriptPath);
    094                    }
    095                    Reader sourceReader = new InputStreamReader(scriptURL.openStream());
    096                    i.eval(sourceReader, i.getNameSpace(), scriptURL.toExternalForm());
    097
    098                    instance = i.get("instance");
    099                    if (instance == null) {
    100                        throw new BeanShellScriptInitializationException("The 'instance' variable was not instantiated");
    101                    }
    102                } catch (EvalError e) {
    103                    throw new BeanShellScriptInitializationException(e);
    104                } catch (IOException e) {
    105                    throw new BeanShellScriptInitializationException(e);
    106                }
    107            }
    108            return instance;
    109        }
    110
    111        public void verify(PicoContainer pico) throws UnsatisfiableDependenciesException {
    112        }
    113    }