001: /*
002: * JBoss, Home of Professional Open Source
003: * Copyright 2005, JBoss Inc., and individual contributors as indicated
004: * by the @authors tag. See the copyright.txt in the distribution for a
005: * full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jbpm.graph.node;
023:
024: import java.util.Collection;
025: import java.util.Iterator;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029: import org.dom4j.Element;
030: import org.hibernate.LockMode;
031: import org.hibernate.Session;
032: import org.jbpm.JbpmContext;
033: import org.jbpm.graph.action.Script;
034: import org.jbpm.graph.def.Node;
035: import org.jbpm.graph.exe.ExecutionContext;
036: import org.jbpm.graph.exe.Token;
037: import org.jbpm.jpdl.xml.JpdlXmlReader;
038: import org.jbpm.jpdl.xml.Parsable;
039: import org.jbpm.util.XmlUtil;
040:
041: public class Join extends Node implements Parsable {
042:
043: private static final long serialVersionUID = 1L;
044:
045: /** specifies wether what type of hibernate lock should be acquired. null value defaults to LockMode.Force */
046: String parentLockMode;
047:
048: /**
049: * specifies if this joinhandler is a discriminator.
050: * a descriminator reactivates the parent when the first
051: * concurrent token enters the join.
052: */
053: boolean isDiscriminator = false;
054:
055: /**
056: * a fixed set of concurrent tokens.
057: */
058: Collection tokenNames = null;
059:
060: /**
061: * a script that calculates concurrent tokens at runtime.
062: */
063: Script script = null;
064:
065: /**
066: * reactivate the parent if the n-th token arrives in the join.
067: */
068: int nOutOfM = -1;
069:
070: public Join() {
071: }
072:
073: public Join(String name) {
074: super (name);
075: }
076:
077: public void read(Element element, JpdlXmlReader jpdlReader) {
078: String lock = element.attributeValue("lock");
079: if ((lock != null) && (lock.equalsIgnoreCase("pessimistic"))) {
080: parentLockMode = LockMode.UPGRADE.toString();
081: }
082: }
083:
084: public void execute(ExecutionContext executionContext) {
085: Token token = executionContext.getToken();
086:
087: boolean isAbleToReactivateParent = token
088: .isAbleToReactivateParent();
089:
090: if (!token.hasEnded()) {
091: token.end(false);
092: }
093:
094: // if this token is not able to reactivate the parent,
095: // we don't need to check anything
096: if (isAbleToReactivateParent) {
097:
098: // the token arrived in the join and can only reactivate
099: // the parent once
100: token.setAbleToReactivateParent(false);
101:
102: Token parentToken = token.getParent();
103:
104: if (parentToken != null) {
105:
106: JbpmContext jbpmContext = executionContext
107: .getJbpmContext();
108: Session session = (jbpmContext != null ? jbpmContext
109: .getSession() : null);
110: if (session != null) {
111: LockMode lockMode = LockMode.FORCE;
112: if (parentLockMode != null) {
113: lockMode = LockMode.parse(parentLockMode);
114: }
115: log
116: .debug("forcing version increment on parent token "
117: + parentToken);
118: session.lock(parentToken, lockMode);
119: }
120:
121: boolean reactivateParent = true;
122:
123: // if this is a discriminator
124: if (isDiscriminator) {
125: // reactivate the parent when the first token arrives in the
126: // join. this must be the first token arriving because otherwise
127: // the isAbleToReactivateParent() of this token should have been false
128: // above.
129: reactivateParent = true;
130:
131: // if a fixed set of tokenNames is specified at design time...
132: } else if (tokenNames != null) {
133: // check reactivation on the basis of those tokenNames
134: reactivateParent = mustParentBeReactivated(
135: parentToken, tokenNames.iterator());
136:
137: // if a script is specified
138: } else if (script != null) {
139:
140: // check if the script returns a collection or a boolean
141: Object result = null;
142: try {
143: result = script.eval(token);
144: } catch (Exception e) {
145: this .raiseException(e, executionContext);
146: }
147: // if the result is a collection
148: if (result instanceof Collection) {
149: // it must be a collection of tokenNames
150: Collection runtimeTokenNames = (Collection) result;
151: reactivateParent = mustParentBeReactivated(
152: parentToken, runtimeTokenNames
153: .iterator());
154:
155: // if it's a boolean...
156: } else if (result instanceof Boolean) {
157: // the boolean specifies if the parent needs to be reactivated
158: reactivateParent = ((Boolean) result)
159: .booleanValue();
160: }
161:
162: // if a nOutOfM is specified
163: } else if (nOutOfM != -1) {
164:
165: int n = 0;
166: // wheck how many tokens already arrived in the join
167: Iterator iter = parentToken.getChildren().values()
168: .iterator();
169: while (iter.hasNext()) {
170: Token concurrentToken = (Token) iter.next();
171: if (this .equals(concurrentToken.getNode())) {
172: n++;
173: }
174: }
175: if (n < nOutOfM) {
176: reactivateParent = false;
177: }
178:
179: // if no configuration is specified..
180: } else {
181: // the default behaviour is to check all concurrent tokens and reactivate
182: // the parent if the last token arrives in the join
183: reactivateParent = mustParentBeReactivated(
184: parentToken, parentToken.getChildren()
185: .keySet().iterator());
186: }
187:
188: // if the parent token needs to be reactivated from this join node
189: if (reactivateParent) {
190:
191: // write to all child tokens that the parent is already reactivated
192: Iterator iter = parentToken.getChildren().values()
193: .iterator();
194: while (iter.hasNext()) {
195: ((Token) iter.next())
196: .setAbleToReactivateParent(false);
197: }
198:
199: // write to all child tokens that the parent is already reactivated
200: ExecutionContext parentContext = new ExecutionContext(
201: parentToken);
202: leave(parentContext);
203: }
204: }
205: }
206: }
207:
208: public boolean mustParentBeReactivated(Token parentToken,
209: Iterator childTokenNameIterator) {
210: boolean reactivateParent = true;
211: while ((childTokenNameIterator.hasNext()) && (reactivateParent)) {
212: String concurrentTokenName = (String) childTokenNameIterator
213: .next();
214:
215: Token concurrentToken = parentToken
216: .getChild(concurrentTokenName);
217:
218: if (concurrentToken.isAbleToReactivateParent()) {
219: log
220: .debug("join will not yet reactivate parent: found concurrent token '"
221: + concurrentToken + "'");
222: reactivateParent = false;
223: }
224: }
225: return reactivateParent;
226: }
227:
228: public Script getScript() {
229: return script;
230: }
231:
232: public void setScript(Script script) {
233: this .script = script;
234: }
235:
236: public Collection getTokenNames() {
237: return tokenNames;
238: }
239:
240: public void setTokenNames(Collection tokenNames) {
241: this .tokenNames = tokenNames;
242: }
243:
244: public boolean isDiscriminator() {
245: return isDiscriminator;
246: }
247:
248: public void setDiscriminator(boolean isDiscriminator) {
249: this .isDiscriminator = isDiscriminator;
250: }
251:
252: public int getNOutOfM() {
253: return nOutOfM;
254: }
255:
256: public void setNOutOfM(int nOutOfM) {
257: this .nOutOfM = nOutOfM;
258: }
259:
260: private static final Log log = LogFactory.getLog(Join.class);
261: }
|