001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.scripting.bsh;
018:
019: import java.io.IOException;
020:
021: import bsh.EvalError;
022:
023: import org.springframework.beans.factory.BeanClassLoaderAware;
024: import org.springframework.scripting.ScriptCompilationException;
025: import org.springframework.scripting.ScriptFactory;
026: import org.springframework.scripting.ScriptSource;
027: import org.springframework.util.Assert;
028: import org.springframework.util.ClassUtils;
029:
030: /**
031: * {@link org.springframework.scripting.ScriptFactory} implementation
032: * for a BeanShell script.
033: *
034: * <p>Typically used in combination with a
035: * {@link org.springframework.scripting.support.ScriptFactoryPostProcessor};
036: * see the latter's javadoc for a configuration example.
037: *
038: * @author Juergen Hoeller
039: * @author Rob Harrop
040: * @since 2.0
041: * @see BshScriptUtils
042: * @see org.springframework.scripting.support.ScriptFactoryPostProcessor
043: */
044: public class BshScriptFactory implements ScriptFactory,
045: BeanClassLoaderAware {
046:
047: private final String scriptSourceLocator;
048:
049: private final Class[] scriptInterfaces;
050:
051: private ClassLoader beanClassLoader = ClassUtils
052: .getDefaultClassLoader();
053:
054: private Class scriptClass;
055:
056: private final Object scriptClassMonitor = new Object();
057:
058: /**
059: * Create a new BshScriptFactory for the given script source.
060: * <p>With this <code>BshScriptFactory</code> variant, the script needs to
061: * declare a full class or return an actual instance of the scripted object.
062: * @param scriptSourceLocator a locator that points to the source of the script.
063: * Interpreted by the post-processor that actually creates the script.
064: */
065: public BshScriptFactory(String scriptSourceLocator) {
066: this (scriptSourceLocator, null);
067: }
068:
069: /**
070: * Create a new BshScriptFactory for the given script source.
071: * <p>The script may either be a simple script that needs a corresponding proxy
072: * generated (implementing the specified interfaces), or declare a full class
073: * or return an actual instance of the scripted object (in which case the
074: * specified interfaces, if any, need to be implemented by that class/instance).
075: * @param scriptSourceLocator a locator that points to the source of the script.
076: * Interpreted by the post-processor that actually creates the script.
077: * @param scriptInterfaces the Java interfaces that the scripted object
078: * is supposed to implement (may be <code>null</code>)
079: */
080: public BshScriptFactory(String scriptSourceLocator,
081: Class[] scriptInterfaces) {
082: Assert.hasText(scriptSourceLocator,
083: "'scriptSourceLocator' must not be empty");
084: this .scriptSourceLocator = scriptSourceLocator;
085: this .scriptInterfaces = scriptInterfaces;
086: }
087:
088: public void setBeanClassLoader(ClassLoader classLoader) {
089: this .beanClassLoader = classLoader;
090: }
091:
092: public String getScriptSourceLocator() {
093: return this .scriptSourceLocator;
094: }
095:
096: public Class[] getScriptInterfaces() {
097: return this .scriptInterfaces;
098: }
099:
100: /**
101: * BeanShell scripts do require a config interface.
102: */
103: public boolean requiresConfigInterface() {
104: return true;
105: }
106:
107: /**
108: * Load and parse the BeanShell script via {@link BshScriptUtils}.
109: * @see BshScriptUtils#createBshObject(String, Class[], ClassLoader)
110: */
111: public Object getScriptedObject(ScriptSource actualScriptSource,
112: Class[] actualInterfaces) throws IOException,
113: ScriptCompilationException {
114:
115: try {
116: Class clazz = null;
117: synchronized (this .scriptClassMonitor) {
118: if (actualScriptSource.isModified()) {
119: // New script content: Let's check whether it evaluates to a Class.
120: Object result = BshScriptUtils.evaluateBshScript(
121: actualScriptSource.getScriptAsString(),
122: actualInterfaces, this .beanClassLoader);
123: if (result instanceof Class) {
124: // A Class: We'll cache the Class here and create an instance
125: // outside of the synchronized block.
126: this .scriptClass = (Class) result;
127: } else {
128: // Not a Class: OK, we'll simply create BeanShell objects
129: // through evaluating the script for every call later on.
130: // For this first-time check, let's simply return the
131: // already evaluated object.
132: return result;
133: }
134: }
135: clazz = this .scriptClass;
136: }
137: if (clazz != null) {
138: // A Class: We need to create an instance for every call.
139: try {
140: return clazz.newInstance();
141: } catch (Throwable ex) {
142: throw new ScriptCompilationException(
143: "Could not instantiate script class: "
144: + clazz, ex);
145: }
146: } else {
147: // Not a Class: We need to evaluate the script for every call.
148: return BshScriptUtils.createBshObject(
149: actualScriptSource.getScriptAsString(),
150: actualInterfaces, this .beanClassLoader);
151: }
152: } catch (EvalError ex) {
153: throw new ScriptCompilationException(
154: "Could not compile BeanShell script: "
155: + actualScriptSource, ex);
156: }
157: }
158:
159: public Class getScriptedObjectType(ScriptSource scriptSource)
160: throws IOException, ScriptCompilationException {
161:
162: try {
163: synchronized (this .scriptClassMonitor) {
164: if (scriptSource.isModified()) {
165: // New script content: Let's check whether it evaluates to a Class.
166: this .scriptClass = BshScriptUtils
167: .determineBshObjectType(scriptSource
168: .getScriptAsString());
169: }
170: return this .scriptClass;
171: }
172: } catch (EvalError ex) {
173: throw new ScriptCompilationException(
174: "Could not compile BeanShell script: "
175: + scriptSource, ex);
176: }
177: }
178:
179: public String toString() {
180: return "BshScriptFactory: script source locator ["
181: + this .scriptSourceLocator + "]";
182: }
183:
184: }
|