package ee.telekom.workflow.executor.marshall;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import ee.telekom.workflow.core.workflowinstance.WorkflowInstance;
import ee.telekom.workflow.core.workflowinstance.WorkflowInstanceStatus;
import ee.telekom.workflow.core.workitem.WorkItem;
import ee.telekom.workflow.core.workitem.WorkItemType;
import ee.telekom.workflow.graph.Environment;
import ee.telekom.workflow.graph.Graph;
import ee.telekom.workflow.graph.GraphInstance;
import ee.telekom.workflow.graph.GraphWorkItem;
import ee.telekom.workflow.graph.Node;
import ee.telekom.workflow.graph.Token;
import ee.telekom.workflow.graph.core.EnvironmentImpl;
import ee.telekom.workflow.graph.core.GraphInstanceImpl;
import ee.telekom.workflow.graph.core.GraphWorkItemImpl;
import ee.telekom.workflow.graph.core.TokenImpl;
import ee.telekom.workflow.util.JsonUtil;
/**
* Helper class to convert between persisted and non-persisted entities.
*
* @author Christian Klock
*/
public class Marshaller{
public static void marshall( GraphInstance instance, WorkflowInstance woin, List<WorkItem> woits, WorkflowInstanceStatus completeStatus ){
woin.setRefNum( instance.getExternalId() );
woin.setWorkflowName( instance.getGraph().getName() );
woin.setWorkflowVersion( instance.getGraph().getVersion() );
woin.setAttributes( serializeAttributes( instance.getEnvironment().getAttributesAsMap() ) );
woin.setHistory( instance.getHistory() );
woin.setState( serializeTokens( instance.getTokens() ) );
woin.setStatus( instance.isCompleted() ? completeStatus : WorkflowInstanceStatus.EXECUTING );
for( GraphWorkItem wi : instance.getWorkItems() ){
woits.add( marshall( wi ) );
}
}
public static GraphInstance unmarshall( WorkflowInstance woin, List<WorkItem> woits, Graph graph ){
GraphInstanceImpl instance = new GraphInstanceImpl();
instance.setExternalId( woin.getRefNum() );
instance.setGraph( graph );
EnvironmentImpl env = deserializeEnv( woin.getAttributes() );
instance.setEnvironment( env );
instance.setHistory( woin.getHistory() );
List<Token> tokens = deserializeTokens( woin.getState(), instance );
instance.setTokens( tokens );
int tokenIdSequence = 0;
for( Token token : tokens ){
if( token.getId() > tokenIdSequence ){
tokenIdSequence = token.getId();
}
}
instance.setTokenIdSquence( tokenIdSequence );
List<GraphWorkItem> workItems = new ArrayList<>();
for( WorkItem woit : woits ){
workItems.add( unmarshall( woit, instance ) );
}
instance.setWorkItems( workItems );
return instance;
}
private static WorkItem marshall( GraphWorkItem workItem ){
WorkItem woit = new WorkItem();
woit.setRefNum( workItem.getExternalId() );
woit.setWoinRefNum( workItem.getExternalGraphInstanceId() );
woit.setTokenId( workItem.getToken().getId() );
woit.setStatus( workItem.getStatus() );
woit.setSignal( workItem.getSignal() );
woit.setDueDate( workItem.getDueDate() );
woit.setBean( workItem.getBean() );
woit.setMethod( workItem.getMethod() );
woit.setRole( workItem.getRole() );
woit.setUserName( workItem.getUser() );
if( WorkItemType.TASK.equals( woit.getType() ) ){
woit.setArguments( JsonUtil.serialize( workItem.getTaskArguments(), false ) );
}
else if( WorkItemType.HUMAN_TASK.equals( woit.getType() ) ){
woit.setArguments( JsonUtil.serialize( workItem.getHumanTaskArguments(), false ) );
}
woit.setResult( serializeResult( workItem.getResult() ) );
return woit;
}
public static String serializeResult( Object result ){
return JsonUtil.serialize( result, true );
}
private static GraphWorkItem unmarshall( WorkItem woit, GraphInstance instance ){
GraphWorkItemImpl workItem = new GraphWorkItemImpl();
workItem.setExternalId( woit.getRefNum() );
workItem.setExternalGraphInstanceId( woit.getWoinRefNum() );
workItem.setToken( findToken( instance.getTokens(), woit.getTokenId() ) );
workItem.setStatus( woit.getStatus() );
workItem.setSignal( woit.getSignal() );
workItem.setDueDate( woit.getDueDate() );
workItem.setBean( woit.getBean() );
workItem.setMethod( woit.getMethod() );
workItem.setRole( woit.getRole() );
workItem.setUser( woit.getUserName() );
if( WorkItemType.TASK.equals( woit.getType() ) ){
workItem.setArguments( deserializeTaskArguments( woit.getArguments() ) );
}
else if( WorkItemType.HUMAN_TASK.equals( woit.getType() ) ){
workItem.setArguments( deserializeHumanTaskArguments( woit.getArguments() ) );
}
workItem.setResult( JsonUtil.deserialize( woit.getResult() ) );
return workItem;
}
public static Object[] deserializeTaskArguments( String json ){
return JsonUtil.deserialize( json, Object[].class );
}
public static Map<String, Object> deserializeHumanTaskArguments( String json ){
return JsonUtil.deserializeHashMap( json, String.class, Object.class );
}
private static TokenState marshall( Token token ){
TokenState state = new TokenState();
state.setId( token.getId() );
state.setNodeId( token.getNode().getId() );
state.setParentId( token.getParent() == null ? null : token.getParent().getId() );
state.setActive( token.isActive() );
return state;
}
public static String serializeAttributes( Map<String, Object> attributes ){
return (attributes == null || attributes.isEmpty()) ? null : JsonUtil.serialize( attributes, false );
}
public static Map<String, Object> deserializeAttributes( String json ){
if( json == null || json.isEmpty() ){
return new HashMap<String, Object>();
}
Map<String, Object> attributes = JsonUtil.deserializeHashMap( json, String.class, Object.class );
return attributes;
}
public static EnvironmentImpl deserializeEnv( String json ){
EnvironmentImpl env = new EnvironmentImpl();
Map<String, Object> attributes = deserializeAttributes( json );
setAttributesMap( env, attributes );
return env;
}
private static String serializeTokens( Collection<Token> tokens ){
List<TokenState> states = new ArrayList<TokenState>();
for( Token token : tokens ){
states.add( marshall( token ) );
}
return serializeTokenStates( states );
}
private static List<Token> deserializeTokens( String json, GraphInstance instance ){
Collection<TokenState> states = deserializeTokenStates( json );
List<Token> tokens = new ArrayList<Token>();
Map<Integer, Token> tokenById = new TreeMap<Integer, Token>();
for( TokenState state : states ){
TokenImpl token = new TokenImpl();
token.setId( state.getId() );
token.setNode( findeNode( instance.getGraph(), state.getNodeId() ) );
token.setInstance( instance );
if( state.getParentId() != null ){
Token parent = tokenById.get( state.getParentId() );
if( parent == null ){
// Should never happen if tokens are (de)-serialized in
// ascending token id order
throw new RuntimeException(
"Token missing or not yet deserialized" );
}
token.setParent( parent );
}
token.setActive( state.isActive() );
tokenById.put( token.getId(), token );
tokens.add( token );
}
return tokens;
}
public static String serializeTokenStates( Collection<TokenState> states ){
return JsonUtil.serializeCollection( states, false, false );
}
public static Collection<TokenState> deserializeTokenStates( String json ){
return JsonUtil.deserializeCollection( json, ArrayList.class, TokenState.class );
}
private static Node findeNode( Graph graph, int id ){
Node result = graph.getNode( id );
if( result != null ){
return result;
}
throw new RuntimeException( "Cannot find node " + id + " on graph "
+ graph.getName() + ":" + graph.getVersion() );
}
private static Token findToken( Collection<Token> tokens, int tokenId ){
for( Token token : tokens ){
if( token.getId() == tokenId ){
return token;
}
}
throw new RuntimeException( "Cannot find token with id " + tokenId );
}
private static void setAttributesMap( Environment env, Map<String, Object> attributes ){
for( Map.Entry<String, Object> entry : attributes.entrySet() ){
env.setAttribute( entry.getKey(), entry.getValue() );
}
}
}