/*
* Copyright 2005-8 Pi4 Technologies Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* Change History:
* 24 Jul 2008 : Initial version created by gary
*/
package org.savara.bpmn2.generation.process;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.savara.bpmn2.internal.generation.BPMN2GenerationException;
import org.savara.bpmn2.internal.generation.BPMN2ModelFactory;
import org.savara.bpmn2.internal.generation.BPMN2NotationFactory;
import org.savara.bpmn2.internal.generation.components.AbstractBPMNActivity;
import org.savara.bpmn2.internal.generation.components.BPMNActivity;
import org.savara.bpmn2.internal.generation.components.BPMNDiagram;
import org.savara.bpmn2.internal.generation.components.BPMNPool;
import org.savara.bpmn2.internal.generation.components.BoundaryEvent;
import org.savara.bpmn2.internal.generation.components.ChoiceActivity;
import org.savara.bpmn2.internal.generation.components.DoActivity;
import org.savara.bpmn2.internal.generation.components.DoBlockActivity;
import org.savara.bpmn2.internal.generation.components.ForkActivity;
import org.savara.bpmn2.internal.generation.components.JoinActivity;
import org.savara.bpmn2.internal.generation.components.ParallelActivity;
import org.savara.bpmn2.internal.generation.components.ReceiveActivity;
import org.savara.bpmn2.internal.generation.components.RepeatActivity;
import org.savara.bpmn2.internal.generation.components.RunActivity;
import org.savara.bpmn2.internal.generation.components.SendActivity;
import org.savara.bpmn2.internal.generation.components.SequenceActivity;
import org.savara.bpmn2.internal.generation.components.ServiceInvocationActivity;
import org.savara.bpmn2.model.TBoundaryEvent;
import org.savara.bpmn2.model.TDefinitions;
import org.savara.bpmn2.model.TErrorEventDefinition;
import org.savara.bpmn2.model.TImport;
import org.savara.bpmn2.model.TItemDefinition;
import org.savara.bpmn2.model.TMessage;
import org.savara.bpmn2.model.TProcess;
import org.savara.bpmn2.model.TReceiveTask;
import org.savara.bpmn2.model.TSendTask;
import org.savara.bpmn2.model.TServiceTask;
import org.savara.common.logging.FeedbackHandler;
import org.savara.common.model.annotation.AnnotationDefinitions;
import org.savara.common.model.annotation.Annotation;
import org.savara.common.model.generator.ModelGenerator;
import org.savara.common.resources.ResourceLocator;
import org.savara.protocol.model.Join;
import org.savara.protocol.model.Fork;
import org.savara.protocol.model.util.InteractionUtil;
import org.scribble.protocol.model.*;
import org.scribble.protocol.util.RunUtil;
/**
* This class represents the Protocol to BPMN2 Process implementation of the model
* generator interface.
*/
public class ProtocolToBPMN2ProcessModelGenerator implements ModelGenerator {
private static final String BPMN_FILE_EXTENSION = ".bpmn";
private boolean _consecutiveIds=false;
private boolean _messageBasedInvocation=false;
private org.savara.bpmn2.model.ObjectFactory _objectFactory=new org.savara.bpmn2.model.ObjectFactory();
private static final Logger logger=Logger.getLogger(ProtocolToBPMN2ProcessModelGenerator.class.getName());
/**
* This method determines whether consecutive ids should be used in the model and
* notation. If false (default), then random unique ids will be used.
*
* @param b Whether to use consecutive ids
*/
public void setUseConsecutiveIds(boolean b) {
_consecutiveIds = b;
}
/**
* This method sets whether message based invocation should be used. If true
* then a process invoking an external service will be represented based on
* separate send and receive tasks (and a choice construct if faults may
* be received). If false, then a service task will be used, with message
* based boundary event definitions to represent received faults.
*
* @param b Whether message based invocation should be used
*/
public void setMessageBasedInvocation(boolean b) {
_messageBasedInvocation = b;
}
/**
* This method indicates whether message based invocation should be
* used.
*
* @return Whether message based invocation should be used
*/
public boolean getMessageBasedInvocation() {
return (_messageBasedInvocation);
}
/**
* This method determines whether the generator is appropriate for
* the specified source and target types.
*
* @param source The source
* @param targetType The target type
* @return Whether the specified types are supported
*/
public boolean isSupported(Object source, String targetType) {
return(source instanceof ProtocolModel &&
((ProtocolModel)source).isLocated() &&
(targetType.equals("bpmn2") || targetType.equals("bpmn")));
}
/**
* {@inheritDoc}
*/
public java.util.Map<String,Object> generate(Object source, FeedbackHandler handler,
final ResourceLocator locator) {
java.util.Map<String,Object> ret=null;
if (source instanceof ProtocolModel) {
ProtocolModel pm=(ProtocolModel)source;
TDefinitions defns=new TDefinitions();
// Setup prefixes
// Obtain any namespace prefix map
java.util.Map<String, String> prefixes=
new java.util.HashMap<String, String>();
java.util.List<Annotation> list=
AnnotationDefinitions.getAnnotations(pm.getProtocol().getAnnotations(),
AnnotationDefinitions.TYPE);
for (Annotation annotation : list) {
if (annotation.getProperties().containsKey(AnnotationDefinitions.NAMESPACE_PROPERTY) &&
annotation.getProperties().containsKey(AnnotationDefinitions.PREFIX_PROPERTY)) {
prefixes.put((String)annotation.getProperties().get(AnnotationDefinitions.NAMESPACE_PROPERTY),
(String)annotation.getProperties().get(AnnotationDefinitions.PREFIX_PROPERTY));
}
}
for (String ns : prefixes.keySet()) {
String prefix=prefixes.get(ns);
defns.getOtherAttributes().put(new QName(null,"xmlns:"+prefix), ns);
}
// Set an id on the definition - not strictly required,
// although the BPMN2 modeler currently seems to want
// it
defns.setId("id-"+pm.getProtocol().getName()+"-"+pm.getProtocol().getLocatedRole());
org.savara.bpmn2.internal.generation.BPMN2ModelFactory model=
new org.savara.bpmn2.internal.generation.BPMN2ModelFactory(defns);
org.savara.bpmn2.internal.generation.BPMN2NotationFactory notation=
new org.savara.bpmn2.internal.generation.BPMN2NotationFactory(model);
model.setUseConsecutiveIds(_consecutiveIds);
notation.setUseConsecutiveIds(_consecutiveIds);
// Find namespace for role
initNamespace(defns, pm);
initImports(defns, pm);
initMessages(defns, pm, prefixes);
BPMN2ModelVisitor visitor=
new BPMN2ModelVisitor(pm.getProtocol().getName(),
model, notation);
generateProcess(pm, visitor, handler, locator);
visitor.completeModels();
ret = new java.util.HashMap<String,Object>();
ret.put(pm.getProtocol().getName()+"_"+pm.getProtocol().getLocatedRole().getName()+
BPMN_FILE_EXTENSION, defns);
}
return(ret);
}
protected void initNamespace(TDefinitions defns, ProtocolModel pm) {
String role=pm.getProtocol().getLocatedRole().getName();
Annotation ann=AnnotationDefinitions.getAnnotationWithProperty(pm.getProtocol().getAnnotations(),
AnnotationDefinitions.INTERFACE, AnnotationDefinitions.ROLE_PROPERTY, role);
if (ann != null) {
defns.setTargetNamespace((String)ann.getProperties().get(AnnotationDefinitions.NAMESPACE_PROPERTY));
// Make sure a namespace prefix is defined for the target namespace
defns.getOtherAttributes().put(new QName(null, "xmlns:tns"), defns.getTargetNamespace());
}
}
protected void initImports(TDefinitions defns, ProtocolModel pm) {
java.util.List<Annotation> anns=AnnotationDefinitions.getAnnotations(pm.getProtocol().getAnnotations(),
AnnotationDefinitions.TYPE);
for (Annotation ann : anns) {
if (ann.getProperties().containsKey(AnnotationDefinitions.LOCATION_PROPERTY)) {
// Add import
TImport imp=new TImport();
imp.setImportType("http://www.w3.org/2001/XMLSchema"); // Assume xsd for now
imp.setLocation((String)ann.getProperties().get(AnnotationDefinitions.LOCATION_PROPERTY));
imp.setNamespace((String)ann.getProperties().get(AnnotationDefinitions.NAMESPACE_PROPERTY));
defns.getImport().add(imp);
}
}
}
protected void initMessages(TDefinitions defns, ProtocolModel pm, java.util.Map<String,String> prefixes) {
for (ImportList il : pm.getImports()) {
if (il instanceof TypeImportList) {
TypeImportList til=(TypeImportList)il;
for (TypeImport ti : til.getTypeImports()) {
TItemDefinition itemDef=new TItemDefinition();
itemDef.setId("ITEM"+ti.getName());
itemDef.setStructureRef(createQName(ti.getDataType().getDetails(), prefixes));
defns.getRootElement().add(_objectFactory.createItemDefinition(itemDef));
TMessage mesg=new TMessage();
mesg.setId("ID"+ti.getName());
mesg.setName(ti.getName());
mesg.setItemRef(new QName(defns.getTargetNamespace(),itemDef.getId(), "tns"));
defns.getRootElement().add(_objectFactory.createMessage(mesg));
}
}
}
}
protected QName createQName(String qname, java.util.Map<String,String> prefixes) {
QName ret=QName.valueOf(qname);
String prefix=prefixes.get(ret.getNamespaceURI());
if (prefix != null) {
ret = new QName(ret.getNamespaceURI(), ret.getLocalPart(), prefix);
}
return (ret);
}
protected void generateProcess(ProtocolModel local, BPMN2ModelVisitor visitor,
FeedbackHandler handler, ResourceLocator locator) {
local.visit(visitor);
}
public class BPMN2ModelVisitor extends DefaultVisitor {
private BPMN2ModelFactory m_modelFactory=null;
private BPMN2NotationFactory m_notationFactory=null;
private String m_choreoName=null;
private java.util.List<BPMNActivity> m_bpmnActivityStack=new java.util.ArrayList<BPMNActivity>();
private java.util.Map<String,BPMNDiagram> m_activityModels=
new java.util.HashMap<String,BPMNDiagram>();
private java.util.Map<Protocol, TProcess> _protocolProcessMap=new java.util.HashMap<Protocol, TProcess>();
private java.util.Map<Protocol, java.util.List<RunActivity>> _pendingCalledActivityId=
new java.util.HashMap<Protocol, java.util.List<RunActivity>>();
/**
* The constructor the BPMN model visitor.
*
*/
public BPMN2ModelVisitor(String choreoName,
BPMN2ModelFactory model, BPMN2NotationFactory notation) {
m_choreoName = choreoName;
m_modelFactory = model;
m_notationFactory = notation;
}
/**
* This method starts visiting the behavior description element.
*
* @param elem The behavior description
*/
public boolean start(Protocol elem) {
try {
BPMNDiagram diagram=getBPMNModel(elem);
BPMNPool pool=diagram.createPool(getPoolName(elem));
if (pool != null) {
_protocolProcessMap.put(elem, pool.getProcess());
// See whether this protocol is the target of run
// activities, and if so, setup the called element id
if (_pendingCalledActivityId.containsKey(elem)) {
java.util.List<RunActivity> list=_pendingCalledActivityId.get(elem);
for (RunActivity ra : list) {
ra.setCalledActivityId(new QName(
m_modelFactory.getDefinitions().getTargetNamespace(),
pool.getProcess().getId(), "tns"));
}
}
}
//diagram.initialize(elem);
pushBPMNActivity(pool);
} catch(Exception e) {
logger.severe("Failed to get state machine " +
"for behavior '"+elem+"': "+e);
}
return(true);
}
protected String getPoolName(Protocol elem) {
if (elem.getParent() instanceof ProtocolModel) {
return(elem.getLocatedRole().getName());
} else {
return(elem.getName()+"_"+elem.getLocatedRole().getName());
}
}
/**
* This method ends visiting the behavior description element.
*
* @param elem The behavior description
*/
public void end(Protocol elem) {
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
umls.childrenComplete();
}
popBPMNActivity();
}
/**
* This method starts visiting the choice element.
*
* @param elem The choice
*/
public boolean start(Choice elem) {
if (!(getBPMNActivity() instanceof ServiceInvocationActivity)) {
try {
pushBPMNActivity(new ChoiceActivity(elem,
getBPMNActivity(), m_modelFactory, m_notationFactory));
} catch(Exception e) {
logger.severe("Failed to create choice state: "+e);
}
}
return(true);
}
/**
* This method ends visiting the choice element.
*
* @param elem The choice
*/
public void end(Choice elem) {
//if (!(getBPMNActivity() instanceof ServiceActivity)) {
popBPMNActivity();
//}
}
/**
* This method starts visiting the parallel element.
*
* @param elem The parallel
*/
public boolean start(Parallel elem) {
try {
pushBPMNActivity(new ParallelActivity(elem,
getBPMNActivity(), m_modelFactory, m_notationFactory));
} catch(Exception e) {
logger.severe("Failed to create parallel state: "+e);
}
return(true);
}
/**
* This method ends visiting the parallel element.
*
* @param elem The parallel
*/
public void end(Parallel elem) {
popBPMNActivity();
}
/**
* This method visits the perform activity.
*
* @param elem The perform
*/
public void accept(Run elem) {
//AbstractBPMNActivity state=null;
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
RunActivity ra=new RunActivity(elem, umls, m_modelFactory, m_notationFactory);
// Check if called protocol is available
Protocol called=RunUtil.getInnerProtocol(elem.getEnclosingProtocol(),
elem.getProtocolReference());
if (called != null) {
TProcess proc=_protocolProcessMap.get(called);
if (proc != null) {
ra.setCalledActivityId(new QName(
m_modelFactory.getDefinitions().getTargetNamespace(),
proc.getId(), "tns"));
} else {
java.util.List<RunActivity> list=_pendingCalledActivityId.get(called);
if (list == null) {
list = new java.util.ArrayList<RunActivity>();
_pendingCalledActivityId.put(called, list);
}
list.add(ra);
}
}
}
}
/**
* This method indicates the start of a
* global escape.
*
* @param elem The global escape
* @return Whether to process the contents
*/
public boolean start(Do elem) {
AbstractBPMNActivity state=null;
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
state = new DoActivity(elem,
umls, m_modelFactory, m_notationFactory);
pushBPMNActivity(state);
DoBlockActivity inline=new DoBlockActivity(state, m_modelFactory, m_notationFactory);
pushBPMNActivity(inline);
}
return(true);
}
/**
* This method indicates the end of a
* try escape.
*
* @param elem The global escape
*/
public void end(Do elem) {
if (getBPMNActivity() instanceof DoBlockActivity) {
popBPMNActivity();
}
popBPMNActivity();
}
/**
* This method indicates the start of a
* catch block.
*
* @param elem The catch block
* @return Whether to process the contents
*/
public boolean start(Interrupt elem) {
if (getBPMNActivity() instanceof DoBlockActivity) {
popBPMNActivity();
}
return(true);
}
/**
* This method indicates the end of a
* catch block.
*
* @param elem The catch block
*/
public void end(Interrupt elem) {
//popBPMNActivity();
}
/**
* This method visits the receive activity.
*
* @param elem The receive
*/
public void accept(Interaction elem) {
// Check if a send
if (InteractionUtil.isSend(elem)) {
if (getMessageBasedInvocation() || !InteractionUtil.isRequest(elem)) {
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
SendActivity sa=
new SendActivity(elem, umls, m_modelFactory, m_notationFactory);
// Register the send to enable links to be established
// with an appropriate receive
BPMNDiagram amodel=umls.getBPMNDiagram();
amodel.registerSendActivity(elem, sa);
TSendTask task=sa.getSendTask();
if (task != null) {
task.setMessageRef(m_modelFactory.getMessageReference(elem));
task.setOperationRef(m_modelFactory.getOperationReference(elem, task.getMessageRef()));
}
}
} else {
// Use service task
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
try {
ServiceInvocationActivity sa=
new ServiceInvocationActivity(elem, umls, m_modelFactory, m_notationFactory);
// Register the send to enable links to be established
// with an appropriate receive
BPMNDiagram amodel=umls.getBPMNDiagram();
amodel.registerSendActivity(elem, sa);
TServiceTask task=sa.getServiceTask();
if (task != null) {
task.setOperationRef(m_modelFactory.getOperationReference(elem,
m_modelFactory.getMessageReference(elem)));
}
pushBPMNActivity(sa);
} catch(Exception e) {
e.printStackTrace();
}
}
}
} else {
// Check if service task is on the stack
ServiceInvocationActivity act=getServiceInvocationActivity();
if (InteractionUtil.isRequest(elem) ||
getMessageBasedInvocation() || act == null) {
if (act != null) {
// Pop the serivce invocation activity, so that the receive
// occurs after the service task
popBPMNActivity();
}
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
ReceiveActivity sa=
new ReceiveActivity(elem, umls, m_modelFactory, m_notationFactory);
// Register the receive to enable links to be established
// with an appropriate send
BPMNDiagram amodel=umls.getBPMNDiagram();
amodel.registerReceiveActivity(elem, sa);
TReceiveTask task=sa.getReceiveTask();
if (task != null) {
task.setMessageRef(m_modelFactory.getMessageReference(elem));
task.setOperationRef(m_modelFactory.getOperationReference(elem, task.getMessageRef()));
}
}
} else {
if (InteractionUtil.isFaultResponse(elem)) {
BPMNActivity umls=getBPMNActivity();
// Create boundary event for fault
TBoundaryEvent event=(TBoundaryEvent)
m_modelFactory.createBoundaryEvent(umls.getContainer());
TErrorEventDefinition eed=new TErrorEventDefinition();
eed.setId(m_modelFactory.createId());
eed.setErrorRef(m_modelFactory.getErrorReference(elem));
// Call this method to establish the error reference on the
// appropriate service interface (if not already defined)
m_modelFactory.getOperationReference(elem, eed.getErrorRef());
event.getEventDefinition().add(_objectFactory.createErrorEventDefinition(eed));
event.setAttachedToRef(new QName(
m_modelFactory.getDefinitions().getTargetNamespace(),
act.getServiceTask().getId(), "tns"));
BoundaryEvent be=new BoundaryEvent(event, act.getStartState(),
m_modelFactory, m_notationFactory);
act.register(umls, be);
} else {
// Need to associate response with service interface - task will then
// be automatically associated with it via the operation reference
m_modelFactory.getOperationReference(elem, m_modelFactory.getMessageReference(elem));
}
}
}
}
protected ServiceInvocationActivity getServiceInvocationActivity() {
ServiceInvocationActivity ret=null;
if (m_bpmnActivityStack.size() > 0) {
BPMNActivity ba=(BPMNActivity)m_bpmnActivityStack.get(0);
if (ba instanceof ServiceInvocationActivity) {
ret = (ServiceInvocationActivity)ba;
} else if (ba instanceof SequenceActivity &&
m_bpmnActivityStack.size() > 1 &&
//m_bpmnActivityStack.get(1) instanceof ChoiceActivity &&
m_bpmnActivityStack.get(1) instanceof ServiceInvocationActivity) {
ret = (ServiceInvocationActivity)m_bpmnActivityStack.get(1);
}
}
return (ret);
}
/**
* This method starts visiting the sequence element.
*
* @param elem The sequence
*/
public boolean start(Block elem) {
try {
pushBPMNActivity(new SequenceActivity(getBPMNActivity(), m_modelFactory, m_notationFactory));
} catch(Exception e) {
logger.severe("Failed to create sequence state: "+e);
}
return(true);
}
/**
* This method ends visiting the sequence element.
*
* @param elem The sequence
*/
public void end(Block elem) {
popBPMNActivity();
}
/**
* This method starts visiting the while element.
*
* @param elem The while
*/
public boolean start(Repeat elem) {
try {
pushBPMNActivity(new RepeatActivity(elem,
getBPMNActivity(), m_modelFactory, m_notationFactory));
pushBPMNActivity(new SequenceActivity(getBPMNActivity(), m_modelFactory, m_notationFactory));
} catch(Exception e) {
logger.severe("Failed to create while Activity: "+e);
}
return(true);
}
/**
* This method ends visiting the while element.
*
* @param elem The while
*/
public void end(Repeat elem) {
popBPMNActivity();
popBPMNActivity();
}
/**
* {@inheritDoc}
*/
public void accept(CustomActivity act) {
if (act instanceof Fork) {
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
new ForkActivity((Fork)act, umls, m_modelFactory, m_notationFactory);
}
} else if (act instanceof Join) {
BPMNActivity umls=getBPMNActivity();
if (umls != null) {
new JoinActivity((Join)act, umls, m_modelFactory, m_notationFactory);
}
}
}
protected BPMNDiagram getBPMNModel(Protocol elem) throws BPMN2GenerationException {
Protocol main=elem.getTopLevelProtocol();
String name=main.getName();
BPMNDiagram ret=(BPMNDiagram)
m_activityModels.get(name);
if (ret == null) {
ret = new BPMNDiagram(m_choreoName, name,
null, m_modelFactory, m_notationFactory);
m_activityModels.put(name, ret);
}
return(ret);
}
/**
* This method pushes the supplied UML activity
* onto a stack.
*
* @param act The activity
*/
protected void pushBPMNActivity(BPMNActivity act) {
m_bpmnActivityStack.add(0, act);
}
/**
* This method returns the UML activity found at the
* top of the stack.
*
* @return The activity
*/
protected BPMNActivity getBPMNActivity() {
BPMNActivity ret=null;
if (m_bpmnActivityStack.size() > 0) {
ret = (BPMNActivity)m_bpmnActivityStack.get(0);
}
return(ret);
}
/**
* This method returns the UML activity from the
* top of the stack.
*
*/
protected void popBPMNActivity() {
BPMNActivity umls=null;
do {
umls = getBPMNActivity();
if (umls != null) {
umls.childrenComplete();
}
if (m_bpmnActivityStack.size() > 0) {
m_bpmnActivityStack.remove(0);
}
} while (umls instanceof ServiceInvocationActivity);
}
/**
* This method completes the construction of the activity
* models.
*
*/
public void completeModels() {
java.util.Iterator<BPMNDiagram> iter=m_activityModels.values().iterator();
while (iter.hasNext()) {
BPMNDiagram amodel=iter.next();
amodel.completeModel();
}
}
}
}