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.groovy;
011
012 import groovy.lang.Binding;
013 import groovy.lang.GroovyClassLoader;
014 import groovy.lang.GroovyCodeSource;
015 import groovy.lang.GroovyObject;
016 import groovy.lang.MissingPropertyException;
017 import groovy.lang.Script;
018
019 import java.io.IOException;
020 import java.io.InputStream;
021 import java.io.Reader;
022 import java.net.URL;
023
024 import org.codehaus.groovy.control.CompilationFailedException;
025 import org.codehaus.groovy.runtime.InvokerHelper;
026 import org.nanocontainer.NanoContainer;
027 import org.nanocontainer.script.NanoContainerMarkupException;
028 import org.nanocontainer.script.ScriptedContainerBuilder;
029 import org.picocontainer.PicoContainer;
030 import org.picocontainer.alternatives.EmptyPicoContainer;
031 import org.nanocontainer.reflection.DefaultNanoPicoContainer;
032
033 /**
034 * {@inheritDoc}
035 * The groovy script has to return an instance of {@link NanoContainer}.
036 * There is an implicit variable named "parent" that may contain a reference to a parent
037 * container. It is recommended to use this as a constructor argument to the instantiated
038 * NanoPicoContainer.
039 *
040 * @author Paul Hammant
041 * @author Aslak Hellesøy
042 * @author Mauro Talevi
043 * @version $Revision: 3144 $
044 */
045 public class GroovyContainerBuilder extends ScriptedContainerBuilder {
046 private Class scriptClass;
047
048 public GroovyContainerBuilder(final Reader script, ClassLoader classLoader) {
049 super(script, classLoader);
050 createGroovyClass();
051 }
052
053 public GroovyContainerBuilder(final URL script, ClassLoader classLoader) {
054 super(script, classLoader);
055 createGroovyClass();
056 }
057
058 // TODO: This should really return NanoContainer using a nano variable in the script. --Aslak
059 protected PicoContainer createContainerFromScript(PicoContainer parentContainer, Object assemblyScope) {
060
061 Binding binding = new Binding();
062 if ( parentContainer == null ){
063 parentContainer = new DefaultNanoPicoContainer(getClassLoader(), new EmptyPicoContainer());
064 }
065 binding.setVariable("parent", parentContainer);
066 binding.setVariable("builder", createGroovyNodeBuilder());
067 binding.setVariable("assemblyScope", assemblyScope);
068 handleBinding(binding);
069 return runGroovyScript(binding);
070 }
071
072 /**
073 * Allows customization of the groovy node builder in descendants.
074 * @return GroovyNodeBuilder
075 */
076 protected GroovyObject createGroovyNodeBuilder() {
077 return new GroovyNodeBuilder();
078 }
079
080 /**
081 * This allows children of this class to add to the default binding.
082 * Might want to add similar or a more generic implementation of this
083 * method to support the other scripting languages.
084 */
085 protected void handleBinding(Binding binding) {
086 // does nothing but adds flexibility for children
087 }
088
089
090 /**
091 * Parses the groovy script into a class. We store the Class instead
092 * of the script proper so that it doesn't invoke race conditions on
093 * multiple executions of the script.
094 */
095 private void createGroovyClass() {
096 try {
097 GroovyClassLoader loader = new GroovyClassLoader(getClassLoader());
098 InputStream scriptIs = getScriptInputStream();
099 GroovyCodeSource groovyCodeSource = new GroovyCodeSource(scriptIs,"nanocontainer.groovy","groovyGeneratedForNanoContainer");
100 scriptClass = loader.parseClass(groovyCodeSource);
101 } catch (CompilationFailedException e) {
102 throw new GroovyCompilationException("Compilation Failed '" + e.getMessage() + "'", e);
103 } catch (IOException e) {
104 throw new NanoContainerMarkupException(e);
105 }
106
107 }
108
109 /**
110 * Executes the groovy script with the given binding.
111 * @param binding Binding
112 * @return PicoContainer
113 */
114 private PicoContainer runGroovyScript(Binding binding){
115 Script script = createGroovyScript(binding);
116
117 Object result = script.run();
118 Object picoVariable;
119 try {
120 picoVariable = binding.getVariable("pico");
121 } catch (MissingPropertyException e) {
122 picoVariable = result;
123 }
124 if (picoVariable == null) {
125 throw new NullPointerException("Groovy Script Variable: pico");
126 }
127
128 if (picoVariable instanceof PicoContainer) {
129 return (PicoContainer) picoVariable;
130 } else if (picoVariable instanceof NanoContainer) {
131 return ((NanoContainer) picoVariable).getPico();
132 } else {
133 throw new NanoContainerMarkupException("Bad type for pico:" + picoVariable.getClass().getName());
134 }
135
136 }
137
138 private Script createGroovyScript(Binding binding) {
139 return InvokerHelper.createScript(scriptClass, binding);
140 }
141 }