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 }