All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.openwfe.org.engine.expressions.sync.GenericSyncExpression Maven / Gradle / Ivy

/*
 * Copyright (c) 2001-2006, John Mettraux, OpenWFE.org
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 * . Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.  
 * 
 * . Redistributions in binary form must reproduce the above copyright notice, 
 *   this list of conditions and the following disclaimer in the documentation 
 *   and/or other materials provided with the distribution.
 * 
 * . Neither the name of the "OpenWFE" nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: GenericSyncExpression.java 2668 2006-05-25 15:15:15Z jmettraux $
 */

//
// GenericSyncExpression.java
//
// [email protected]
//
// generated with 
// jtmpl 1.1.00 16.08.2003 John Mettraux ([email protected])
//

package openwfe.org.engine.expressions.sync;

import java.util.Collections;

import openwfe.org.Utils;
import openwfe.org.engine.workitem.InFlowWorkItem;
import openwfe.org.engine.expressions.ApplyException;
import openwfe.org.engine.expressions.ReplyException;
import openwfe.org.engine.expressions.FlowExpression;
import openwfe.org.engine.expressions.FlowExpressionId;
import openwfe.org.engine.expressions.CompositeFlowExpression;


/**
 * All the concurrence (multi-choice) patterns may be resolved by this
 * sync expression.
 * 
*
 * <concurrence
 *   sync="generic"
 *   count="x|*"
 *   merge="first|last|highest|lowest"
 *   merge-type="mix|override"
 *   remaining="cancel|forget"
 * >
 * 
*
    *
  • count: on how many concurrence branches should we wait ?
  • *
  • merge: which branch shall have priority for the merge ?
  • *
  • merge-type: mixing fields or completely obsfucating with fields * of the 'winning' incoming workitem ?
  • *
  • remaining: when count is reached, should we cancel or let * other branches die (forgotten) ?
  • *
* *

CVS Info : *
$Author: jmettraux $ *
$Id: GenericSyncExpression.java 2668 2006-05-25 15:15:15Z jmettraux $
* * @author [email protected] */ public class GenericSyncExpression extends ClassicalSyncExpression { private final static org.apache.log4j.Logger log = org.apache.log4j.Logger .getLogger(GenericSyncExpression.class.getName()); // // CONSTANTS & co /** * Since OpenWFE 1.5.0, this generic sync expression is able * to act like the other sync expressions (whose classes became * obsolete). */ public final static String A_SYNC = "sync"; public final static String A_COUNT = "count"; public final static String A_MERGE = "merge"; public final static String A_MERGE_TYPE = "merge-type"; public final static String A_REMAINING = "remaining"; public final static String MG_FIRST = "first"; public final static String MG_LAST = "last"; public final static String MG_HIGHEST = "highest"; public final static String MG_LOWEST = "lowest"; public final static String DEFAULT_MG = MG_FIRST; public final static String MT_MIX = "mix"; public final static String MT_OVERRIDE = "override"; public final static String DEFAULT_MT = MT_MIX; public final static String REM_CANCEL = "cancel"; public final static String REM_FORGET = "forget"; public final static String DEFAULT_REM = REM_CANCEL; // // FIELDS // config fields private int count = -1; private String merge = null; private String mergeType = null; private String remaining = null; // run fields private java.util.List childrenAltitudes = null; private int replyCount = 0; private InFlowWorkItem overridenWorkitem = null; private java.util.List incomingWorkitems = null; private java.util.List unreadyQueue = null; // // CONSTRUCTORS /* public GenericSyncExpression () { super(); } */ /** * This method is called by SyncUtils.determineSyncExpression(). * It parses the attributes of the parent [synchable]Expression to * determine on this sync should behave. */ public void init (final SynchableExpression se, final java.util.List childList, final InFlowWorkItem wi) { // // do the job if (log.isDebugEnabled()) log.debug("init() on behalf of "+se.getId()); // // count final String sCount = se.lookupAttribute(A_COUNT, wi); try { this.count = Integer.parseInt(sCount); } catch (final Exception e) { this.count = -1; } if (log.isDebugEnabled()) log.debug("init() count : "+this.count); // // merge this.merge = se.lookupAttribute(A_MERGE, wi); if ( ! (MG_FIRST.equals(this.merge) || MG_LAST.equals(this.merge) || MG_HIGHEST.equals(this.merge) || MG_LOWEST.equals(this.merge))) { this.merge = DEFAULT_MG; } if (log.isDebugEnabled()) log.debug("init() merge : '"+this.merge+"'"); // // merge type this.mergeType = se.lookupAttribute(A_MERGE_TYPE, wi); if ( ! (MT_MIX.equals(this.mergeType) || MT_OVERRIDE.equals(this.mergeType))) { this.mergeType = DEFAULT_MT; } if (log.isDebugEnabled()) log.debug("init() merge : '"+this.mergeType+"'"); // // remaining this.remaining = se.lookupAttribute(A_REMAINING, wi); if ( ! (REM_CANCEL.equals(this.remaining) || REM_FORGET.equals(this.remaining))) { this.remaining = DEFAULT_REM; } if (log.isDebugEnabled()) log.debug("init() remaining : '"+this.remaining+"'"); // // prepare workitem queues if (childList == null) { this.incomingWorkitems = new java.util.LinkedList(); this.unreadyQueue = new java.util.LinkedList(); } else { this.incomingWorkitems = new java.util.ArrayList(childList.size()); this.unreadyQueue = new java.util.ArrayList(childList.size()); } // // keep track of the order among children // (which is the highest ?) if (this.merge.equals(MG_LAST) || this.merge.equals(MG_FIRST)) // // don't do the following [unnecessary operations] { return; } this.childrenAltitudes = listAltitudes(childList); } private java.util.List listAltitudes (final java.util.List childList) { if (childList == null) return new java.util.LinkedList(); final java.util.List result = new java.util.ArrayList(childList.size()); final java.util.Iterator it = childList.iterator(); while (it.hasNext()) { final FlowExpressionId fei = (FlowExpressionId)it.next(); //log.debug("listAltitudes() adding "+fei); result.add(fei); } return result; } // // BEAN METHODS // config fields public int getCount () { return this.count; } public String getMerge () { return this.merge; } public String getMergeType () { return this.mergeType; } public String getRemaining () { return this.remaining; } public void setCount (final int i) { this.count = i; } public void setMerge (final String s) { this.merge = s; } public void setMergeType (final String s) { this.mergeType = s; } public void setRemaining (final String s) { this.remaining = s; } // run fields /** * Returns how many children have replied so far. */ public int getReplyCount () { return this.replyCount; } /** * When 'merge' is set to 'override', this field contains the * temporary result of the sync. */ public InFlowWorkItem getOverridenWorkitem () { return this.overridenWorkitem; } /** * The list of workitems already arrived back from the children. */ public java.util.List getIncomingWorkitems () { return this.incomingWorkitems; } /** * The returned list is used by this expression to keep track of * the 'altitude' of each child. * This measurement is used in case of 'highest' or 'lowest' merge * style. */ public java.util.List getChildrenAltitudes () { return this.childrenAltitudes; } /** * A sync expression is not ready until its unreadyQueue is flushed and * set to null ; * replies to an unready sync expression are stored in this unreadyQueue. */ public java.util.List getUnreadyQueue () { return this.unreadyQueue; } public void setReplyCount (final int i) { this.replyCount = i; } public void setOverridenWorkitem (final InFlowWorkItem wi) { this.overridenWorkitem = wi; } public void setIncomingWorkitems (final java.util.List l) { this.incomingWorkitems = l; } public void setChildrenAltitudes (final java.util.List l) { this.childrenAltitudes = l; } public void setUnreadyQueue (final java.util.List l) { this.unreadyQueue = l; } // // METHODS from SyncExpression /** * From the verb 'to ready' : readies the SyncExpression instance * (eventually flushes its reply queue). */ public synchronized void ready (final SynchableExpression se) throws ReplyException { if (this.unreadyQueue != null) { final java.util.List queue = this.unreadyQueue; this.unreadyQueue = null; // // sync expression is now considered as ready. //synchronized (se) //{ se.storeItself(); //} final java.util.Iterator it = queue.iterator(); while (it.hasNext()) { final InFlowWorkItem wi = (InFlowWorkItem)it.next(); //log.debug("ready() replying for "+wi.getLastExpressionId()); this.reply(se, wi); } } } public void addChild (final FlowExpressionId fei) { super.addChild(fei); if (this.childrenAltitudes != null) this.childrenAltitudes.add(fei.getWorkflowInstanceId()); } private void removeChild (final FlowExpressionId fei) { // // 1st attempt : with the fei boolean removed = getChildren().remove(fei); if (removed) { log.debug("removeChild() removed (fei)"); return; } // // 2nd attempt : with the workflowInstanceId FlowExpressionId toRemove = null; final java.util.Iterator it = this.getChildren().iterator(); while (it.hasNext()) { final FlowExpressionId child = (FlowExpressionId)it.next(); if (child.getWorkflowInstanceId() .equals(fei.getWorkflowInstanceId())) { toRemove = child; break; } } if (toRemove != null) { log.debug("removeChild() removed (wfid)"); getChildren().remove(toRemove); } } public synchronized void reply (final SynchableExpression se, final InFlowWorkItem wi) throws ReplyException { if (this.unreadyQueue != null) // // sync expression not yet ready { log.debug("reply() sync expression not ready : queueing reply"); this.unreadyQueue.add(wi.clone()); //synchronized (se) //{ se.storeItself(); //} return; } if (log.isDebugEnabled()) log.debug("reply() from "+wi.getLastExpressionId()); final int childCount = this.getChildren().size(); //log.debug("reply() child count : "+childCount); //log.debug("reply() before :\n"+dumpChildren()); // // is sync over ? boolean syncIsOver = false; receiveWorkitem(wi); removeChild(wi.getLastExpressionId()); syncIsOver = syncIsOver || getChildren().size() < 1; syncIsOver = syncIsOver || (getCount() > 0 && this.replyCount >= this.count); if (log.isDebugEnabled()) { log.debug ("reply() delta is "+(childCount - this.getChildren().size())); //log.debug // ("reply() after :\n"+dumpChildren()); log.debug ("reply() is sync over for "+se.getId()+" ? "+syncIsOver); } if (syncIsOver) { terminateSync(se); return; } // // sync is not over, let's save the synchable expression //synchronized (se) //{ se.storeItself(); //} } /* * Stores the workitem for later synchronization (there are optimizations * here for some sync tactics like MT_OVERRIDE) */ private void receiveWorkitem (final InFlowWorkItem wi) { //log.debug // ("receiveWorkitem() wi comes from "+wi.getLastExpressionId()); //log.debug // ("receiveWorkitem() replyCount is "+this.replyCount); // //if (this.overridenWorkitem != null) //{ // log.debug // ("receiveWorkitem() overridenWi is "+ // this.overridenWorkitem.getLastExpressionId()); //} // //log.debug // ("receiveWorkitem() incomingWorkitems size is "+ // getIncomingWorkitems().size()); this.replyCount++; // // if MT_MIX if (getMergeType().equals(MT_MIX)) { if (getMerge().equals(MG_HIGHEST) || getMerge().equals(MG_LOWEST)) { getIncomingWorkitems().add(wi.clone()); return; } if (this.overridenWorkitem == null) { this.overridenWorkitem = (InFlowWorkItem)wi.clone(); return; } if (getMerge().equals(MG_FIRST)) { this.overridenWorkitem = MergeUtils.merge (wi, this.overridenWorkitem); return; } // // else if MG_LAST this.overridenWorkitem = MergeUtils.merge (this.overridenWorkitem, wi); return; } // // else MT_OVERRIDE if (this.overridenWorkitem == null) { this.overridenWorkitem = (InFlowWorkItem)wi.clone(); return; } if (getMerge().equals(MG_FIRST)) { if (this.overridenWorkitem != null) return; this.overridenWorkitem = (InFlowWorkItem)wi.clone(); return; } if (getMerge().equals(MG_LAST)) { this.overridenWorkitem = (InFlowWorkItem)wi.clone(); return; } // MG_HIGHEST or MG_LOWEST final int currentAltitude = determineAltitude(this.overridenWorkitem); final int incomingAltitude = determineAltitude(wi); //log.debug("receiveWorkitem() currentAltitude "+currentAltitude); //log.debug("receiveWorkitem() incomingAltitude "+incomingAltitude); if (incomingAltitude < 0) { throw new IllegalArgumentException ("Not waiting on the incoming workitem"); } else if (getMerge().equals(MG_HIGHEST)) { if (incomingAltitude < currentAltitude) this.overridenWorkitem = (InFlowWorkItem)wi.clone(); } else // if (getMerge().equals(MG_LOWEST)) { if (incomingAltitude > currentAltitude) this.overridenWorkitem = (InFlowWorkItem)wi.clone(); } } /* * Returns at which altitude the participant expression that emitted * the workitem is located in the chilren altitude list. */ private int determineAltitude (final InFlowWorkItem wi) { final String wfiid = wi.getLastExpressionId().getWorkflowInstanceId(); return getChildrenAltitudes().indexOf(wfiid); } /* * Does the work of terminating the synchronization. */ private void terminateSync (final SynchableExpression se) throws ReplyException { // // should we cancel or forget (stub) remaining children ? try { treatRemainingChildren(se); } catch (final ApplyException ae) { log.warn ("Failed to '"+getRemaining()+ "' remaining children", ae); throw new ReplyException ("Failed to '"+getRemaining()+ "' remaining children", ae); } // // determine resulting workitem InFlowWorkItem resultingWorkitem = null; if (this.overridenWorkitem != null) { resultingWorkitem = this.overridenWorkitem; } else //if (getMergeType().equals(MT_MIX)) { //log.debug("terminateSync() doing the merge..."); java.util.Collections.sort(getChildrenAltitudes()); if (getMerge().equals(MG_HIGHEST)) java.util.Collections.reverse(getChildrenAltitudes()); final java.util.Map answerMap = determineAnswerMap(); final java.util.Iterator it = getChildrenAltitudes().iterator(); while (it.hasNext()) { final String wfiid = (String)it.next(); //log.debug("terminateSync() considering altitude "+wfiid); final InFlowWorkItem workitem = (InFlowWorkItem)answerMap.get(wfiid); if (workitem == null) continue; //log.debug // ("terminateSync() workitem is "+ // workitem.getLastExpressionId()); resultingWorkitem = MergeUtils.merge(resultingWorkitem, workitem); //log.debug // ("terminateSync() resulting workitem is "+ // resultingWorkitem.getLastExpressionId()); } } // // this log output should be disabled after a certain // test period. // if (resultingWorkitem == null) log.warn("reply() resultingWorkitem is null"); // // reply to parent expression se.replyToParent(resultingWorkitem); } /* * Returns a mapping fei: wi */ private java.util.Map determineAnswerMap () { final java.util.Map result = new java.util.HashMap(getIncomingWorkitems().size()); final java.util.Iterator it = getIncomingWorkitems().iterator(); while (it.hasNext()) { final InFlowWorkItem wi = (InFlowWorkItem)it.next(); //log.debug // ("determineAnswerMap() found answer for "+ // wi.getLastExpressionId().getWorkflowInstanceId()); result.put(wi.getLastExpressionId().getWorkflowInstanceId(), wi); } return result; } private void treatRemainingChildren (final SynchableExpression se) throws ApplyException { if (log.isDebugEnabled()) { log.debug ("treatRemainingChildren() - "+getChildren().size()+ " children in total"); } final java.util.List alreadyReplied = new java.util.ArrayList(getIncomingWorkitems().size()); java.util.Iterator it = getIncomingWorkitems().iterator(); while (it.hasNext()) { final InFlowWorkItem wi = (InFlowWorkItem)it.next(); alreadyReplied.add(wi.getLastExpressionId()); } it = getChildren().iterator(); while (it.hasNext()) { final FlowExpressionId fei = (FlowExpressionId)it.next(); if (alreadyReplied.contains(fei)) continue; if (log.isDebugEnabled()) { log.debug ("treatRemainingChildren() '"+getRemaining()+"' on "+fei); } if (REM_CANCEL.equals(getRemaining())) { se.getExpressionPool().childCancel(/*(FlowExpression)se, */fei); } else { se.getExpressionPool().forget(fei); } } } // // METHODS /* private String dumpChildren () { final StringBuffer sb = new StringBuffer(); sb .append("---children---") .append(" instance : ") .append(this) .append("\n"); final java.util.Iterator it = this.getChildren().iterator(); while (it.hasNext()) { sb .append(" * ") .append(it.next().toString()) .append("\n"); } sb.append("---c---"); return sb.toString(); } */ }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy