package org.jbpm.pvm.internal.wire.descriptor; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jbpm.api.JbpmException; import org.jbpm.internal.log.Log; import org.jbpm.pvm.internal.env.EnvironmentImpl; import org.jbpm.pvm.internal.script.ScriptManager; import org.jbpm.pvm.internal.util.ReflectUtil; import org.jbpm.pvm.internal.wire.Descriptor; import org.jbpm.pvm.internal.wire.JbpmClassNotFoundException; import org.jbpm.pvm.internal.wire.WireContext; import org.jbpm.pvm.internal.wire.WireDefinition; import org.jbpm.pvm.internal.wire.WireException; import org.jbpm.pvm.internal.wire.operation.FieldOperation; import org.jbpm.pvm.internal.wire.operation.Operation; import org.jbpm.pvm.internal.wire.operation.PropertyOperation; /** *
This {@link Descriptor} creates and initializes an object. * Objects can be instantiated from a constructor or from a method invocation.
* *The way to create an object is specified one of these methods (see creating objects): *
This method is used when {@link #getClassName()}!=null && {@link #getMethodName()}==null
.
The {@link #construct(WireContext)} method creates a new object * from a constructor matching the given arguments * (specified with {@link #setArgDescriptors(List)}).
* * *The name of the method to call is specified by the method attribute.
*{@link #getFactoryObjectName()}!=null
: the object with the name factoryObjectName will be fetched from the context.{@link #getFactoryDescriptor()}!=null
: the object will be created from the factory descriptor.The object returned by {@link #construct(WireContext)} is the object returned by the method invocation.
* * *If the auto wiring is enabled for the object ({@link #isAutoWireEnabled()}==true
),
* the WireContext will try to look for objects with the same name as the fields in the class.
* If it finds an object with that name, and if it is assignable to the field's type, it is automatically injected,
* without the need for explicit {@link FieldOperation} that specifies the injection in the wiring xml.
If the auto wiring is enabled and the WireContext finds an object with the name of a field, but not assignable to this field, * a warning message is generated.
* *Auto-wiring is disabled by default.
* *Field injection or property injection are done after the auto-wiring. For more information, see {@link Operation}.
* *If a field was injected by auto-wiring, its value can be overridden by specifying * a {@link FieldOperation} or {@link PropertyOperation} operation.
* * @author Tom Baeyens * @author Guillaume Porcher (documentation) * */ public class ObjectDescriptor extends AbstractDescriptor implements Descriptor { private static final long serialVersionUID = 1L; private static Log log = Log.getLog(ObjectDescriptor.class.getName()); protected String className = null; /** specifies the object reference on which the method will be invoked. * Either className, objectName or a descriptor has to be specified. * * TODO check if this member can be replaced by a RefDescriptor in the factoryDescriptor member. * * */ String factoryObjectName = null; protected String expr; protected String lang; /** specifies the object on which to invoke the method. * Either className, objectName or a descriptor has to be specified. */ protected Descriptor factoryDescriptor = null; protected String methodName = null; /** map to db as a component */ protected Listtrue
, auto-wiring is performed (see {@link #autoWire(Object, WireContext)}). Fields and properties injections are then performed.
*
*/
public void initialize(Object object, WireContext wireContext) {
try {
// specified operations takes precedence over auto-wiring.
// e.g. in case there is a collision between
// a field or property injection and an autowired value,
// the field or property injections should win.
// That is why autowiring is done first
if (isAutoWireEnabled) {
autoWire(object, wireContext);
}
if (operations!=null) {
for(Operation operation: operations) {
operation.apply(object, wireContext);
}
}
} catch (Exception e) {
throw new WireException("couldn't initialize object '"+(name!=null ? name : className)+"': "+e.getMessage(), e);
}
}
public Class> getType(WireDefinition wireDefinition) {
if (className!=null) {
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
return Class.forName(className, true, classLoader);
} catch (Exception e) {
throw new WireException("couldn't load class '"+className+"'", e);
}
}
Descriptor descriptor = null;
if (factoryDescriptor!=null) {
descriptor = factoryDescriptor;
} else if (factoryObjectName!=null) {
descriptor = wireDefinition.getDescriptor(factoryObjectName);
}
if (descriptor!=null) {
Class> factoryClass = descriptor.getType(wireDefinition);
if (factoryClass!=null) {
Method method = ReflectUtil.findMethod(factoryClass, methodName, argDescriptors, null);
if (method!=null) {
return method.getReturnType();
}
}
}
return null;
}
/**
* Auto wire object present in the context and the specified object's fields.
* @param object object on which auto-wiring is performed.
* @param wireContext context in which the wiring objects are searched.
*/
protected void autoWire(Object object, WireContext wireContext) {
Class> clazz = object.getClass();
while (clazz!=null) {
Field[] declaredFields = clazz.getDeclaredFields();
if (declaredFields!=null) {
for (Field field: declaredFields) {
if (! Modifier.isStatic(field.getModifiers())) {
String fieldName = field.getName();
Class> fieldType = field.getType();
Object autoWireValue = null;
if ("environment".equals(fieldName)) {
autoWireValue = EnvironmentImpl.getCurrent();
} else if ( ("context".equals(fieldName))
|| ("wireContext".equals(fieldName))
) {
autoWireValue = wireContext;
} else if (wireContext.has(fieldName)) {
autoWireValue = wireContext.get(fieldName);
} else {
autoWireValue = wireContext.get(fieldType);
}
// if auto wire value has not been found in current context,
// search in environment
if (autoWireValue == null) {
EnvironmentImpl currentEnvironment = EnvironmentImpl.getCurrent();
if (currentEnvironment != null) {
autoWireValue = currentEnvironment.get(fieldName);
if (autoWireValue == null) {
autoWireValue = currentEnvironment.get(fieldType);
}
}
}
if (autoWireValue!=null) {
try {
if (log.isTraceEnabled()) log.trace("auto wiring field "+fieldName+" in "+name);
ReflectUtil.set(field, object, autoWireValue);
} catch (JbpmException e) {
if(e.getCause() instanceof IllegalArgumentException) {
log.info("WARNING: couldn't auto wire "+fieldName+" (of type "+fieldType.getName()+") " +
"with value "+autoWireValue + " (of type "+autoWireValue.getClass().getName()+")");
} else {
log.info("WARNING: couldn't auto wire "+fieldName+" with value "+autoWireValue);
}
}
}
}
}
}
clazz = clazz.getSuperclass();
}
}
/**
* Creates a list of arguments (objects) from a list of argument descriptors.
* @param wireContext context used to create objects.
* @param argDescriptors list of argument descriptors.
* @return a list of object created from the descriptors.
* @throws Exception
*/
public static Object[] getArgs(WireContext wireContext, List