package org.jbpm.pvm.internal.wire.operation;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jbpm.internal.log.Log;
import org.jbpm.pvm.internal.env.Context;
import org.jbpm.pvm.internal.env.Environment;
import org.jbpm.pvm.internal.util.FilterListener;
import org.jbpm.pvm.internal.util.Listener;
import org.jbpm.pvm.internal.util.Observable;
import org.jbpm.pvm.internal.wire.Descriptor;
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.descriptor.ArgDescriptor;
/**
* subscribes to an {@link Observable observable}.
*
* <p>The target object can be a {@link Listener}
* or a specific method to call can be specified (by {@link #setMethodName(String)})</p>
*
* <p>The event can be filtered by specifying a {@link Context} (with {@link #setContextName(String)}),
* objects to observe (with {@link #setObjectNames(List)}) and events to observe
* (with {@link #setEventNames(List)}). If the objects or events are not specified,
* then all objects and events are observed.</p>
*
* <p>The {@link #setWireEvents(boolean)} specifies if the object or the {@link Descriptor} events
* should be observed.</p>
*
* @author Tom Baeyens
* @author Guillaume Porcher (documentation)
*
*/
public class SubscribeOperation implements Operation {
private static final long serialVersionUID = 1L;
private static Log log = Log.getLog(SubscribeOperation.class.getName());
String contextName = null;
List<String> eventNames = null;
boolean wireEvents = false;
List<String> objectNames = null;
String methodName = null;
List<ArgDescriptor> argDescriptors = null;
public void apply(Object target, WireContext targetWireContext) {
Listener listener = null;
// if a method has to be invoked, rather then using the observable interface
if (methodName!=null) {
listener = new MethodInvokerListener(
methodName,
argDescriptors,
targetWireContext,
target
);
} else {
try {
listener = (Listener) target;
} catch (ClassCastException e) {
throw new WireException("couldn't subscribe object "+target+ " because it is not a Listener");
}
}
// if there is a filter specified on the event names
if ( (eventNames!=null)
&& (! eventNames.isEmpty())
) {
listener = new FilterListener(listener, eventNames);
}
// identify the wireContext
WireContext wireContext = null;
if (contextName!=null) {
Environment environment = Environment.getCurrent();
if (environment!=null) {
try {
wireContext = (WireContext) environment.getContext(contextName);
if (wireContext==null) {
throw new WireException("couldn't subscribe because context "+contextName+" doesn't exist");
}
} catch (ClassCastException e) {
throw new WireException("couldn't subscribe because context "+contextName+" is not a WireContext", e);
}
} else {
throw new WireException("couldn't get context "+contextName+" for subscribe because no environment available in context "+targetWireContext);
}
} else {
wireContext = targetWireContext;
}
if (wireEvents) {
WireDefinition wireDefinition = wireContext.getWireDefinition();
// if there are objectNames specified
if (objectNames!=null) {
// subscribe to the descriptors for the all objectNames
for (String objectName: objectNames) {
Descriptor descriptor = wireDefinition.getDescriptor(objectName);
subscribe(listener, descriptor);
}
// if no objectNames are specified, subscribe to all the descriptors
} else {
Set<Descriptor> descriptors = new HashSet<Descriptor>(wireDefinition.getDescriptors().values());
for(Descriptor descriptor: descriptors) {
subscribe(listener, descriptor);
}
}
} else if ( (objectNames!=null)
&& (!objectNames.isEmpty())
) {
// for every objectName
for (String objectName: objectNames) {
// subscribe to the objects themselves
Object object = wireContext.get(objectName);
if (object==null) {
throw new WireException("couldn't subscribe to object in context "+wireContext.getName()+": object "+objectName+" unavailable");
}
if (! (object instanceof Observable)) {
throw new WireException("couldn't subscribe to object in context "+wireContext.getName()+": object "+objectName+" ("+object.getClass().getName()+") isn't "+Observable.class.getName());
}
subscribe(listener, (Observable)object);
}
} else {
// subscribe to the context
subscribe(listener, wireContext);
}
}
void subscribe(Listener listener, Observable observable) {
log.trace("adding "+listener+" as listener to "+observable);
observable.addListener(listener);
}
/**
* Gets the list of argDescriptor used to create the arguments given to the method (only if a specific method has to be called).
*/
public List<ArgDescriptor> getArgDescriptors() {
return argDescriptors;
}
/**
* Sets the list of argDescriptor used to create the arguments given to the method.
*/
public void setArgDescriptors(List<ArgDescriptor> argDescriptors) {
this.argDescriptors = argDescriptors;
}
/**
* Gets the list of events to listen to.
*/
public List<String> getEventNames() {
return eventNames;
}
/**
* Sets the list of events to listen to.
*/
public void setEventNames(List<String> eventNames) {
this.eventNames = eventNames;
}
/**
* Gets the name of the method to invoke when an event is received.
*/
public String getMethodName() {
return methodName;
}
/**
* Sets the name of the method to invoke when an event is received.
*/
public void setMethodName(String methodName) {
this.methodName = methodName;
}
/**
* Gets the name of the WireContext where the Observable should be found.
*/
public String getContextName() {
return contextName;
}
/**
* Sets the name of the WireContext where the Observable should be found.
*/
public void setContextName(String contextName) {
this.contextName = contextName;
}
/**
* Gets the list of name of the Observable objects to observe.
*/
public List<String> getObjectNames() {
return objectNames;
}
/**
* Sets the list of name of the Observable objects to observe.
*/
public void setObjectNames(List<String> objectNames) {
this.objectNames = objectNames;
}
/**
* <p><code>true</code> if the target object will listen to Descriptor related events.</p>
* <p><code>false</code> if the target object will listen to the object instance events.</p>
*/
public boolean isWireEvents() {
return wireEvents;
}
/**
* Sets if the object should listen to descriptor events or to events fired by the named object.
* <p><code>true</code> if the target object will listen to Descriptor related events.</p>
* <p><code>false</code> if the target object will listen to the object instance events.</p>
*/
public void setWireEvents(boolean wireEvents) {
this.wireEvents = wireEvents;
}
}