001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: ElementFactory.java 3716 2007-04-11 06:21:18Z gbevin $
007: */
008: package com.uwyn.rife.engine;
009:
010: import java.beans.PropertyDescriptor;
011: import java.io.File;
012: import java.io.UnsupportedEncodingException;
013: import java.lang.reflect.InvocationTargetException;
014: import java.lang.reflect.Method;
015: import java.net.URL;
016: import java.net.URLDecoder;
017: import java.util.Collection;
018: import java.util.logging.Logger;
019:
020: import com.uwyn.rife.engine.exceptions.*;
021: import com.uwyn.rife.instrument.RifeAgent;
022: import com.uwyn.rife.ioc.HierarchicalProperties;
023: import com.uwyn.rife.rep.Rep;
024: import com.uwyn.rife.resources.ResourceFinder;
025: import com.uwyn.rife.resources.ResourceFinderClasspath;
026: import com.uwyn.rife.tools.*;
027: import com.uwyn.rife.tools.exceptions.BeanUtilsException;
028: import com.uwyn.rife.tools.exceptions.ConversionException;
029: import com.uwyn.rife.tools.exceptions.FileUtilsErrorException;
030:
031: class ElementFactory {
032: static final ElementFactory INSTANCE = new ElementFactory();
033:
034: private static final String SCRIPT_EXT_JAVASCRIPT = ".js";
035: private static final String SCRIPT_EXT_JACL = ".jacl";
036: private static final String SCRIPT_EXT_JYTHON = ".py";
037: private static final String SCRIPT_EXT_PNUTS = ".pnut";
038: private static final String SCRIPT_EXT_RUBY = ".rb";
039: private static final String SCRIPT_EXT_BEANSHELL = ".bsh";
040: private static final String SCRIPT_EXT_GROOVY = ".groovy";
041: private static final String SCRIPT_EXT_JANINO = ".janino";
042: private static final String[] SCRIPT_EXTENSIONS = new String[] {
043: SCRIPT_EXT_JAVASCRIPT, SCRIPT_EXT_JACL, SCRIPT_EXT_JYTHON,
044: SCRIPT_EXT_PNUTS, SCRIPT_EXT_RUBY, SCRIPT_EXT_BEANSHELL,
045: SCRIPT_EXT_GROOVY, SCRIPT_EXT_JANINO };
046:
047: private static String mJythonClassPath = "";
048: private static ScriptLoaderGroovy mScriptLoaderGroovy = null;
049: private static ScriptLoaderJanino mScriptLoaderJanino = null;
050:
051: private ResourceFinder mResourceFinder = ResourceFinderClasspath
052: .getInstance();
053:
054: private ElementFactory() {
055: }
056:
057: ResourceFinder getResourceFinder() {
058: return mResourceFinder;
059: }
060:
061: static ElementType detectElementType(String implementation) {
062: if (null == implementation)
063: return null;
064:
065: // if there are path seperators, it's a script
066: if (implementation.indexOf("/") != -1) {
067: return ElementType.SCRIPT;
068: }
069:
070: // check if the implementation name ends with a script file extension
071: for (String extension : SCRIPT_EXTENSIONS) {
072: if (implementation.endsWith(extension)) {
073: return ElementType.SCRIPT;
074: }
075: }
076:
077: // it's thus a java class name
078: return ElementType.JAVA_CLASS;
079: }
080:
081: Class getJavaClass(String declarationName, String implementation)
082: throws EngineException {
083: try {
084: // try to look it up as a java element and compile it if needed
085: Class element_class = null;
086: if (getClass().getClassLoader() instanceof EngineClassLoader) {
087: element_class = ((EngineClassLoader) getClass()
088: .getClassLoader()).loadClass(implementation,
089: true, true);
090: } else {
091: HierarchicalProperties properties = Rep.getProperties();
092: if (!properties
093: .contains(RifeAgent.AGENT_ACTIVE_PROPERTY)
094: && (!properties
095: .contains("engineclassloader.enabled") || StringUtils
096: .convertToBoolean(properties.get(
097: "engineclassloader.enabled")
098: .getValueString()))) {
099: Logger
100: .getLogger(
101: getClass().getPackage().getName())
102: .warning(
103: "The element implementation class "
104: + implementation
105: + " is not being loaded by EngineClassLoader which means that continuations will not work. You should be executing your application with the com.uwyn.rife.test.RunWithEngineClassLoader class.");
106: }
107: element_class = getClass().getClassLoader().loadClass(
108: implementation);
109: }
110:
111: return element_class;
112: } catch (ClassNotFoundException e2) {
113: throw new ElementImplementationNotFoundException(
114: declarationName, implementation, e2);
115: }
116: }
117:
118: ElementAware getJavaInstance(String declarationName,
119: String implementation) throws EngineException {
120: ElementAware element = null;
121:
122: try {
123: // try to look it up as a java element and compile it if needed
124: Class element_class = getJavaClass(declarationName,
125: implementation);
126: element = (ElementAware) element_class.newInstance();
127: } catch (IllegalAccessException e2) {
128: // this should not happen
129: throw new ElementImplementationInstantiationException(
130: declarationName, implementation, e2);
131: } catch (InstantiationException e2) {
132: // this should not happen
133: throw new ElementImplementationInstantiationException(
134: declarationName, implementation, e2);
135: }
136:
137: return element;
138: }
139:
140: ElementSupport getInstance(final ElementInfo elementInfo,
141: final boolean injectProperties) throws EngineException {
142: if (null == elementInfo)
143: throw new IllegalArgumentException(
144: "elementInfo can't be null.");
145:
146: final ElementAware element_aware;
147:
148: if (ElementType.JAVA_CLASS == elementInfo.getType()) {
149: try {
150: // try to obtain the element as a bytecode class from the classpath
151: element_aware = getJavaInstance(elementInfo
152: .getDeclarationName(), elementInfo
153: .getImplementation());
154: } catch (ElementCompilationFailedException e) {
155: // this should not happen
156: throw e;
157: } catch (Throwable e) {
158: throw new ElementImplementationInstantiationException(
159: elementInfo.getDeclarationName(), elementInfo
160: .getImplementation(), e);
161: }
162: } else if (ElementType.JAVA_INSTANCE == elementInfo.getType()) {
163: try {
164: element_aware = (Element) elementInfo
165: .getImplementationBlueprint().clone();
166: } catch (CloneNotSupportedException e) {
167: throw new ElementImplementationInstantiationException(
168: elementInfo.getDeclarationName(), elementInfo
169: .getImplementation(), e);
170: }
171: }
172: // handle bean scripting framework scripts
173: else if (ElementType.SCRIPT == elementInfo.getType()) {
174: if (elementInfo.getImplementation().endsWith(
175: SCRIPT_EXT_GROOVY)) {
176: if (null == mScriptLoaderGroovy) {
177: mScriptLoaderGroovy = new ScriptLoaderGroovy(
178: mResourceFinder);
179: }
180:
181: element_aware = mScriptLoaderGroovy
182: .getInstance(elementInfo);
183: } else if (elementInfo.getImplementation().endsWith(
184: SCRIPT_EXT_JANINO)) {
185: if (null == mScriptLoaderJanino) {
186: mScriptLoaderJanino = new ScriptLoaderJanino(
187: mResourceFinder);
188: }
189:
190: element_aware = mScriptLoaderJanino
191: .getInstance(elementInfo);
192: } else if (elementInfo.getImplementation().endsWith(
193: SCRIPT_EXT_JAVASCRIPT)) {
194: String code = getScriptCode(mResourceFinder,
195: elementInfo);
196: ScriptedEngine engine = new ScriptedEngineRhino(code);
197:
198: element_aware = new ElementScripted(engine);
199: } else {
200: String language = null;
201:
202: if (elementInfo.getImplementation().endsWith(
203: SCRIPT_EXT_JACL)) {
204: language = "jacl";
205: } else if (elementInfo.getImplementation().endsWith(
206: SCRIPT_EXT_JYTHON)) {
207: language = "jython";
208: addRifeJarsToClasspathProperty();
209: } else if (elementInfo.getImplementation().endsWith(
210: SCRIPT_EXT_PNUTS)) {
211: language = "pnuts";
212: } else if (elementInfo.getImplementation().endsWith(
213: SCRIPT_EXT_RUBY)) {
214: language = "ruby";
215: } else if (elementInfo.getImplementation().endsWith(
216: SCRIPT_EXT_BEANSHELL)) {
217: language = "beanshell";
218: }
219:
220: String code = getScriptCode(mResourceFinder,
221: elementInfo);
222: ScriptedEngine engine = new ScriptedEngineBSF(language,
223: code);
224:
225: element_aware = new ElementScripted(engine);
226: }
227: } else {
228: throw new ElementImplementationInstantiationException(
229: elementInfo.getDeclarationName(), elementInfo
230: .getImplementation(), null);
231: }
232:
233: ElementSupport element = null;
234: if (element_aware instanceof ElementSupport) {
235: element = (ElementSupport) element_aware;
236: } else {
237: element = new ElementSupport();
238: }
239: element.setElementAware(element_aware);
240: element.setElementInfo(elementInfo);
241:
242: // handle IoC setter injection
243: if (injectProperties) {
244: injectProperties(elementInfo, element_aware);
245: }
246:
247: return element;
248: }
249:
250: static void injectProperties(final ElementInfo elementInfo,
251: final ElementAware elementAware)
252: throws PropertiesInjectionException {
253: Collection<String> property_names = elementInfo
254: .getInjectablePropertyNames();
255: if (elementInfo.getInjectablePropertyNames().size() > 0) {
256: String[] property_names_array = new String[property_names
257: .size()];
258: property_names.toArray(property_names_array);
259: try {
260: BeanUtils.processProperties(BeanUtils.SETTERS,
261: elementAware.getClass(), property_names_array,
262: null, null, new BeanPropertyProcessor() {
263: public boolean gotProperty(String name,
264: PropertyDescriptor descriptor)
265: throws IllegalAccessException,
266: IllegalArgumentException,
267: InvocationTargetException {
268: Method write = descriptor
269: .getWriteMethod();
270: Class type = write.getParameterTypes()[0];
271: try {
272: write.invoke(elementAware, Convert
273: .toType(elementInfo
274: .getProperty(name),
275: type));
276: } catch (ConversionException e) {
277: throw new PropertyInjectionException(
278: elementInfo
279: .getDeclarationName(),
280: elementAware.getClass(),
281: name, e);
282: }
283:
284: return true;
285: }
286: });
287: } catch (BeanUtilsException e) {
288: throw new PropertiesInjectionException(elementInfo
289: .getDeclarationName(), elementAware.getClass(),
290: e);
291: }
292: }
293: }
294:
295: static URL getScriptUrl(ResourceFinder resourceFinder,
296: ElementInfo elementInfo) throws EngineException {
297: URL sourcename_url = resourceFinder.getResource(elementInfo
298: .getImplementation());
299:
300: if (null == sourcename_url) {
301: sourcename_url = resourceFinder
302: .getResource(EngineClassLoader.DEFAULT_IMPLEMENTATIONS_PATH
303: + elementInfo.getImplementation());
304: }
305:
306: if (null == sourcename_url) {
307: throw new ElementImplementationNotFoundException(
308: elementInfo.getDeclarationName(), elementInfo
309: .getImplementation(), null);
310: }
311:
312: return sourcename_url;
313: }
314:
315: static String getScriptCode(ResourceFinder resourceFinder,
316: ElementInfo elementInfo) throws EngineException {
317: URL sourcename_url = getScriptUrl(resourceFinder, elementInfo);
318:
319: try {
320: return FileUtils.readString(sourcename_url);
321: } catch (FileUtilsErrorException e) {
322: throw new ElementImplementationUnreadableException(
323: elementInfo.getDeclarationName(), elementInfo
324: .getImplementation(), e);
325: }
326: }
327:
328: private void addRifeJarsToClasspathProperty() {
329: // only set the jython class path property when it's not null
330: if (mJythonClassPath != null) {
331: // try to generate the class path additions when the class path var is empty
332: if (0 == mJythonClassPath.length()) {
333: URL resource = this
334: .getClass()
335: .getClassLoader()
336: .getResource(
337: "com/uwyn/rife/engine/ElementFactory.class");
338: if (resource != null
339: && resource.getProtocol().equals("jar")) {
340: String resource_path = null;
341: try {
342: resource_path = URLDecoder.decode(resource
343: .getPath(), "ISO-8859-1");
344: String prefix = "file:";
345: String jar_filename = resource_path.substring(
346: prefix.length(), resource_path
347: .indexOf('!'));
348: File jar_file = new File(jar_filename);
349: File jar_directory = jar_file.getParentFile();
350: if (jar_directory != null
351: && jar_directory.isDirectory()) {
352: StringBuilder java_class_path = new StringBuilder(
353: System
354: .getProperty("java.class.path"));
355: String jar_directory_path = URLDecoder
356: .decode(jar_directory.getPath(),
357: "ISO-8859-1");
358: // check if the class path doesn't already contain the jars in the WEB-INF/lib directory
359: if (-1 == java_class_path
360: .indexOf(jar_directory_path)) {
361: // add the jars in the WEB-INF/lib directory to the class path property
362: String[] jar_filenames = jar_directory
363: .list();
364: for (String jar_filenames_entry : jar_filenames) {
365: if (jar_filenames_entry
366: .endsWith(".jar")) {
367: java_class_path
368: .append(File.pathSeparator);
369: java_class_path
370: .append(jar_directory_path);
371: java_class_path
372: .append(File.separator);
373: java_class_path
374: .append(jar_filenames_entry);
375: }
376: }
377:
378: // check if the WEB-INF/classes directory exists and should be added too
379: if (jar_directory_path
380: .endsWith("WEB-INF/lib")) {
381: StringBuilder classes_directory_path = new StringBuilder(
382: jar_directory.getParent());
383: classes_directory_path
384: .append(File.separator);
385: classes_directory_path
386: .append("classes");
387: File classes_directory = new File(
388: classes_directory_path
389: .toString());
390: if (classes_directory.exists()
391: && classes_directory
392: .isDirectory()) {
393: java_class_path
394: .append(File.pathSeparator);
395: java_class_path
396: .append(classes_directory_path);
397: }
398: }
399:
400: // store the constructed class path for quick reuse at later invocations
401: mJythonClassPath = java_class_path
402: .toString();
403: }
404:
405: // set the new class path property
406: System.setProperty("java.class.path",
407: mJythonClassPath);
408: return;
409: }
410: } catch (UnsupportedEncodingException e) {
411: // should never fail, it's a standard encoding
412: }
413: }
414: } else {
415: System.setProperty("java.class.path", mJythonClassPath);
416: return;
417: }
418:
419: // this means that the class path additions couldn't be find
420: // and thus the class path property should remain unchanged
421: mJythonClassPath = null;
422: }
423: }
424: }
|