/*
* Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: ElementFactory.java 3918 2008-04-14 17:35:35Z gbevin $
*/
package com.uwyn.rife.engine;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.logging.Logger;
import com.uwyn.rife.engine.exceptions.*;
import com.uwyn.rife.instrument.RifeAgent;
import com.uwyn.rife.ioc.HierarchicalProperties;
import com.uwyn.rife.rep.Rep;
import com.uwyn.rife.resources.ResourceFinder;
import com.uwyn.rife.resources.ResourceFinderClasspath;
import com.uwyn.rife.tools.*;
import com.uwyn.rife.tools.exceptions.BeanUtilsException;
import com.uwyn.rife.tools.exceptions.ConversionException;
import com.uwyn.rife.tools.exceptions.FileUtilsErrorException;
class ElementFactory
{
static final ElementFactory INSTANCE = new ElementFactory();
private static final String SCRIPT_EXT_JAVASCRIPT = ".js";
private static final String SCRIPT_EXT_JACL = ".jacl";
private static final String SCRIPT_EXT_JYTHON = ".py";
private static final String SCRIPT_EXT_PNUTS = ".pnut";
private static final String SCRIPT_EXT_RUBY = ".rb";
private static final String SCRIPT_EXT_BEANSHELL = ".bsh";
private static final String SCRIPT_EXT_GROOVY = ".groovy";
private static final String SCRIPT_EXT_JANINO = ".janino";
private static final String[] SCRIPT_EXTENSIONS = new String[] {
SCRIPT_EXT_JAVASCRIPT, SCRIPT_EXT_JACL, SCRIPT_EXT_JYTHON,
SCRIPT_EXT_PNUTS, SCRIPT_EXT_RUBY, SCRIPT_EXT_BEANSHELL,
SCRIPT_EXT_GROOVY, SCRIPT_EXT_JANINO};
private static String mJythonClassPath = "";
private static ScriptLoaderGroovy mScriptLoaderGroovy = null;
private static ScriptLoaderJanino mScriptLoaderJanino = null;
private ResourceFinder mResourceFinder = ResourceFinderClasspath.getInstance();
private ElementFactory()
{
}
ResourceFinder getResourceFinder()
{
return mResourceFinder;
}
static ElementType detectElementType(String implementation)
{
if (null == implementation) return null;
// if there are path seperators, it's a script
if (implementation.indexOf("/") != -1)
{
return ElementType.SCRIPT;
}
// check if the implementation name ends with a script file extension
for (String extension : SCRIPT_EXTENSIONS)
{
if (implementation.endsWith(extension))
{
return ElementType.SCRIPT;
}
}
// it's thus a java class name
return ElementType.JAVA_CLASS;
}
Class getJavaClass(String declarationName, String implementation)
throws EngineException
{
try
{
// try to look it up as a java element and compile it if needed
Class element_class = null;
if (getClass().getClassLoader() instanceof EngineClassLoader)
{
element_class = ((EngineClassLoader)getClass().getClassLoader()).loadClass(implementation, true, true);
}
else
{
HierarchicalProperties properties = Rep.getProperties();
if (!properties.contains(RifeAgent.AGENT_ACTIVE_PROPERTY) &&
(!properties.contains("engineclassloader.enabled") ||
StringUtils.convertToBoolean(properties.get("engineclassloader.enabled").getValueString())))
{
Logger.getLogger(getClass().getPackage().getName()).warning("The element implementation class "+implementation +" 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.");
}
element_class = getClass().getClassLoader().loadClass(implementation);
}
return element_class;
}
catch (ClassNotFoundException e2)
{
throw new ElementImplementationNotFoundException(declarationName, implementation, e2);
}
}
ElementAware getJavaInstance(String declarationName, String implementation)
throws EngineException
{
ElementAware element = null;
try
{
// try to look it up as a java element and compile it if needed
Class element_class = getJavaClass(declarationName, implementation);
element = (ElementAware) element_class.newInstance();
}
catch (IllegalAccessException e2)
{
// this should not happen
throw new ElementImplementationInstantiationException(declarationName, implementation, e2);
}
catch (InstantiationException e2)
{
// this should not happen
throw new ElementImplementationInstantiationException(declarationName, implementation, e2);
}
return element;
}
ElementSupport getInstance(final ElementInfo elementInfo, final boolean injectProperties)
throws EngineException
{
if (null == elementInfo) throw new IllegalArgumentException("elementInfo can't be null.");
final ElementAware element_aware;
if (ElementType.JAVA_CLASS == elementInfo.getType())
{
try
{
// try to obtain the element as a bytecode class from the classpath
element_aware = getJavaInstance(elementInfo.getDeclarationName(), elementInfo.getImplementation());
}
catch (ElementCompilationFailedException e)
{
// this should not happen
throw e;
}
catch (Throwable e)
{
throw new ElementImplementationInstantiationException(elementInfo.getDeclarationName(), elementInfo.getImplementation(), e);
}
}
else if (ElementType.JAVA_INSTANCE == elementInfo.getType())
{
try
{
element_aware = (Element)elementInfo.getImplementationBlueprint().clone();
}
catch (CloneNotSupportedException e)
{
throw new ElementImplementationInstantiationException(elementInfo.getDeclarationName(), elementInfo.getImplementation(), e);
}
}
// handle bean scripting framework scripts
else if (ElementType.SCRIPT == elementInfo.getType())
{
if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_GROOVY))
{
if (null == mScriptLoaderGroovy)
{
mScriptLoaderGroovy = new ScriptLoaderGroovy(mResourceFinder);
}
element_aware = mScriptLoaderGroovy.getInstance(elementInfo);
}
else if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_JANINO))
{
if (null == mScriptLoaderJanino)
{
mScriptLoaderJanino = new ScriptLoaderJanino(mResourceFinder);
}
element_aware = mScriptLoaderJanino.getInstance(elementInfo);
}
else if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_JAVASCRIPT))
{
String code = getScriptCode(mResourceFinder, elementInfo);
ScriptedEngine engine = new ScriptedEngineRhino(code);
element_aware = new ElementScripted(engine);
}
else
{
String language = null;
if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_JACL))
{
language = "jacl";
}
else if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_JYTHON))
{
language = "jython";
addRifeJarsToClasspathProperty();
}
else if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_PNUTS))
{
language = "pnuts";
}
else if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_RUBY))
{
language = "ruby";
}
else if (elementInfo.getImplementation().endsWith(SCRIPT_EXT_BEANSHELL))
{
language = "beanshell";
}
String code = getScriptCode(mResourceFinder, elementInfo);
ScriptedEngine engine = new ScriptedEngineBSF(language, code);
element_aware = new ElementScripted(engine);
}
}
else
{
throw new ElementImplementationInstantiationException(elementInfo.getDeclarationName(), elementInfo.getImplementation(), null);
}
ElementSupport element = null;
if (element_aware instanceof ElementSupport)
{
element = (ElementSupport)element_aware;
}
else
{
element = new ElementSupport();
}
element.setElementAware(element_aware);
element.setElementInfo(elementInfo);
// handle IoC setter injection
if (injectProperties)
{
injectProperties(elementInfo, element_aware);
}
return element;
}
static void injectProperties(final ElementInfo elementInfo, final ElementAware elementAware)
throws PropertiesInjectionException
{
Collection<String> property_names = elementInfo.getInjectablePropertyNames();
if (elementInfo.getInjectablePropertyNames().size() > 0)
{
String[] property_names_array = new String[property_names.size()];
property_names.toArray(property_names_array);
try
{
BeanUtils.processProperties(BeanUtils.SETTERS, elementAware.getClass(), property_names_array, null, null, new BeanPropertyProcessor() {
public boolean gotProperty(String name, PropertyDescriptor descriptor)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method write = descriptor.getWriteMethod();
Class type = write.getParameterTypes()[0];
try
{
write.invoke(elementAware, Convert.toType(elementInfo.getProperty(name), type));
}
catch (ConversionException e)
{
throw new PropertyInjectionException(elementInfo.getDeclarationName(), elementAware.getClass(), name, e);
}
return true;
}
});
}
catch (BeanUtilsException e)
{
throw new PropertiesInjectionException(elementInfo.getDeclarationName(), elementAware.getClass(), e);
}
}
}
static URL getScriptUrl(ResourceFinder resourceFinder, ElementInfo elementInfo)
throws EngineException
{
URL sourcename_url = resourceFinder.getResource(elementInfo.getImplementation());
if (null == sourcename_url)
{
sourcename_url = resourceFinder.getResource(EngineClassLoader.DEFAULT_IMPLEMENTATIONS_PATH+elementInfo.getImplementation());
}
if (null == sourcename_url)
{
throw new ElementImplementationNotFoundException(elementInfo.getDeclarationName(), elementInfo.getImplementation(), null);
}
return sourcename_url;
}
static String getScriptCode(ResourceFinder resourceFinder, ElementInfo elementInfo)
throws EngineException
{
URL sourcename_url = getScriptUrl(resourceFinder, elementInfo);
try
{
return FileUtils.readString(sourcename_url);
}
catch (FileUtilsErrorException e)
{
throw new ElementImplementationUnreadableException(elementInfo.getDeclarationName(), elementInfo.getImplementation(), e);
}
}
private void addRifeJarsToClasspathProperty()
{
// only set the jython class path property when it's not null
if (mJythonClassPath != null)
{
// try to generate the class path additions when the class path var is empty
if (0 == mJythonClassPath.length())
{
URL resource = this.getClass().getClassLoader().getResource("com/uwyn/rife/engine/ElementFactory.class");
if (resource != null &&
resource.getProtocol().equals("jar"))
{
String resource_path = null;
try
{
resource_path = URLDecoder.decode(resource.getPath(), "ISO-8859-1");
String prefix = "file:";
String jar_filename = resource_path.substring(prefix.length(), resource_path.indexOf('!'));
File jar_file = new File(jar_filename);
File jar_directory = jar_file.getParentFile();
if (jar_directory != null &&
jar_directory.isDirectory())
{
StringBuilder java_class_path = new StringBuilder(System.getProperty("java.class.path"));
String jar_directory_path = URLDecoder.decode(jar_directory.getPath(), "ISO-8859-1");
// check if the class path doesn't already contain the jars in the WEB-INF/lib directory
if (-1 == java_class_path.indexOf(jar_directory_path))
{
// add the jars in the WEB-INF/lib directory to the class path property
String[] jar_filenames = jar_directory.list();
for (String jar_filenames_entry : jar_filenames)
{
if (jar_filenames_entry.endsWith(".jar"))
{
java_class_path.append(File.pathSeparator);
java_class_path.append(jar_directory_path);
java_class_path.append(File.separator);
java_class_path.append(jar_filenames_entry);
}
}
// check if the WEB-INF/classes directory exists and should be added too
if (jar_directory_path.endsWith("WEB-INF/lib"))
{
StringBuilder classes_directory_path = new StringBuilder(jar_directory.getParent());
classes_directory_path.append(File.separator);
classes_directory_path.append("classes");
File classes_directory = new File(classes_directory_path.toString());
if (classes_directory.exists() &&
classes_directory.isDirectory())
{
java_class_path.append(File.pathSeparator);
java_class_path.append(classes_directory_path);
}
}
// store the constructed class path for quick reuse at later invocations
mJythonClassPath = java_class_path.toString();
}
// set the new class path property
System.setProperty("java.class.path", mJythonClassPath);
return;
}
}
catch (UnsupportedEncodingException e)
{
// should never fail, it's a standard encoding
}
}
}
else
{
System.setProperty("java.class.path", mJythonClassPath);
return;
}
// this means that the class path additions couldn't be find
// and thus the class path property should remain unchanged
mJythonClassPath = null;
}
}
}