1
2
3
4
5
6
7
8 package net.kwfgrid.gwes;
9
10 import net.kwfgrid.gworkflowdl.structure.*;
11 import net.kwfgrid.gwes.workflowanalyzer.DoubleDistribution;
12 import net.kwfgrid.gwes.util.StringUtils;
13
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import org.apache.log4j.Logger;
18 import org.jdom.Element;
19 import org.jdom.Namespace;
20 import org.jdom.IllegalNameException;
21 import org.jdom.Document;
22 import org.jaxen.SimpleNamespaceContext;
23
24
25
26
27
28
29 public class TransitionOccurrence {
30
31
32
33
34 private final static Logger logger = Logger.getLogger(TransitionOccurrence.class);
35
36 public final Transition transition;
37
38 private Activity activity;
39
40 public TokenParameterList tokens;
41
42
43
44
45 private final GenericProperties props;
46
47
48
49
50 private final Document context;
51
52
53
54
55 private final SimpleNamespaceContext nsContext;
56
57 public boolean hasXPathEdgeExpressions;
58 public final boolean simulation;
59
60
61
62
63 private final String ID;
64
65
66
67
68 private String IDwithOutputs;
69
70 private DoubleDistribution probabilityDistribution;
71
72
73
74
75
76
77 public TransitionOccurrence(Transition transition) throws WorkflowFormatException {
78 this.transition = transition;
79 this.activity = null;
80 this.tokens = new TokenParameterList();
81 hasXPathEdgeExpressions = false;
82 simulation = false;
83 props = Factory.newProperties();
84 context = new Document(new Element("data",JdomString.wfSpace));
85 nsContext = new DefaultNamespaceContext();
86
87
88
89 int ignoreDataGroups = TokenUtils.getIgnoreDataGroupsCode(transition);
90
91 String dataGroup = null;
92 ArrayList<String> dataGroups = null;
93 String[] combineDataGroupsEdgeExpressions = TokenUtils.getCombineDataGroupsEdgeExpressions(transition);
94
95 if (combineDataGroupsEdgeExpressions != null) dataGroups = new ArrayList<String>();
96
97
98 updateNamespaceContextFromTransitionProperties();
99
100
101 StringBuffer idBuffer = new StringBuffer(transition.getID());
102
103 Edge[] edges;
104
105 edges = transition.getReadEdges();
106 for (Edge edge : edges) {
107 Token token = TokenUtils.getNextUnlockedToken(edge,true,dataGroup,ignoreDataGroups);
108 if (token == null) {
109 throw new WorkflowFormatException("No suitable token available on readPlace \"" + edge.getPlaceID() + "\" for data.group \"" + dataGroup + "\".");
110 } else {
111
112 addToContext(token, edge);
113
114 updatePropertiesFromToken(token);
115
116 TokenParameter tp = new TokenParameter(edge, token, TokenParameter.Scope.READ);
117 tokens.add(tp);
118
119 dataGroup = TokenUtils.updateDataGroup(tp, ignoreDataGroups, combineDataGroupsEdgeExpressions, dataGroups);
120
121 idBuffer.append(",r:").append(token.getID());
122 }
123 }
124
125
126 edges = transition.getInEdges();
127 for (Edge edge : edges) {
128 Token token = TokenUtils.getNextUnlockedToken(edge,false,dataGroup,ignoreDataGroups);
129 if (token == null) {
130 throw new WorkflowFormatException("No suitable token available on inputPlace \"" + edge.getPlaceID() + "\" for data.group \"" + dataGroup + "\".");
131 } else {
132
133 addToContext(token, edge);
134
135 updatePropertiesFromToken(token);
136
137 TokenParameter tp = new TokenParameter(edge, token, TokenParameter.Scope.INPUT);
138 tokens.add(tp);
139
140 dataGroup = TokenUtils.updateDataGroup(tp, ignoreDataGroups, combineDataGroupsEdgeExpressions, dataGroups);
141
142 idBuffer.append(",i:").append(token.getID());
143 }
144 }
145
146
147 edges = transition.getWriteEdges();
148 for (Edge edge : edges) {
149 Token token = TokenUtils.getNextUnlockedToken(edge,true,dataGroup,ignoreDataGroups);
150 if (token == null) {
151 throw new WorkflowFormatException("There is no unlocked Token on Place '"+edge.getPlaceID()+"' available!");
152 } else {
153 if (!hasXPathEdgeExpressions) {
154 String edgeExpression = edge.getExpression();
155 if (edgeExpression != null && edgeExpression.indexOf('$')!=-1) hasXPathEdgeExpressions = true;
156 }
157
158 addToContext(token, edge);
159
160 updatePropertiesFromToken(token);
161
162 TokenParameter tp = new TokenParameter(edge, token, TokenParameter.Scope.WRITE);
163 tokens.add(tp);
164
165 dataGroup = TokenUtils.updateDataGroup(tp, ignoreDataGroups, combineDataGroupsEdgeExpressions, dataGroups);
166
167 idBuffer.append(",w:").append(token.getID());
168 }
169 }
170
171
172 edges = transition.getOutEdges();
173 for (Edge edge : edges) {
174 if (!hasXPathEdgeExpressions) {
175 String edgeExpression = edge.getExpression();
176 if (edgeExpression != null && edgeExpression.indexOf('$')!=-1) hasXPathEdgeExpressions = true;
177 }
178 tokens.add(new TokenParameter(edge, TokenParameter.Scope.OUTPUT));
179 }
180
181
182 updateDataGroupProperty(dataGroup, dataGroups);
183
184
185 updateResourceAllocationGroupProperty();
186
187
188 ID = idBuffer.toString();
189 updateID();
190
191
192 if(logger.isDebugEnabled()) {
193 logger.debug("constructed "+this.toString());
194 }
195 }
196
197
198
199
200 public void lockTokens() {
201 if (logger.isDebugEnabled()) { logger.debug("lockTokens["+ID+"] ..."); }
202
203 for (TokenParameter token : tokens) {
204 switch (token.scope) {
205 case READ:
206 continue;
207 case INPUT:
208 token.token.lock(transition);
209 break;
210 case WRITE:
211 continue;
212 case OUTPUT:
213 if (token.token != null) token.token.lock(transition);
214 break;
215 }
216 }
217 }
218
219
220
221
222 public void unlockTokens() {
223 if (logger.isDebugEnabled()) { logger.debug("unlockTokens["+ID+"] ..."); }
224
225 for (TokenParameter token : tokens) {
226 switch (token.scope) {
227 case READ:
228 continue;
229 case INPUT:
230 token.token.unlock();
231 break;
232 case WRITE:
233 continue;
234 case OUTPUT:
235 if (token.token != null) token.token.unlock();
236 break;
237 }
238 }
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252
253 public int checkConditions(ConditionChecker checker) throws WorkflowFormatException {
254 if (transition.getConditions().length<1) return ConditionChecker.CONDITION_TRUE;
255 if (logger.isDebugEnabled()) { logger.debug("checkConditions["+ID+"] ..."); }
256 int transCondition = checker.check(this);
257 if (logger.isDebugEnabled()) { logger.debug("checkConditions["+ID+"] ... : "+transCondition); }
258 return transCondition;
259 }
260
261
262
263
264 public void removeInputTokens() {
265 for (TokenParameter token : tokens) {
266 switch (token.scope) {
267 case READ:
268 continue;
269 case INPUT:
270 if (logger.isDebugEnabled()) {
271 logger.debug("removeInputTokens["+ID+"]: removing token 'i:"+token.token.getID() +"' from place '"+token.edge.getPlaceID()+"'");
272 }
273 token.edge.getPlace().removeToken(token.token);
274 break;
275 case WRITE:
276 case OUTPUT:
277 return;
278 }
279 }
280 }
281
282
283
284
285
286
287
288 public void putOutputReservationTokens() throws CapacityException {
289 for (TokenParameter tp : tokens) {
290 switch(tp.scope) {
291 case OUTPUT:
292 if (logger.isDebugEnabled()) {
293 logger.debug("putOutputReservationTokens["+ID+"]: putting reservation token to place '"+tp.edge.getPlaceID()+"'");
294 }
295 Token dummyToken = Factory.newToken();
296 dummyToken.lock(transition);
297 tp.edge.getPlace().addToken(dummyToken);
298 break;
299 }
300 }
301 }
302
303
304
305
306 public void removeOutputReservationTokens() {
307 for (TokenParameter token : tokens) {
308 switch (token.scope) {
309 case READ:
310 case INPUT:
311 case WRITE:
312 continue;
313 case OUTPUT:
314 if (token.token != null && token.token.isLockedBy(transition)) {
315 if (logger.isDebugEnabled()) {
316 logger.debug("removeOutputReservationTokens["+ID+"]: removing reservation token from place '"+token.edge.getPlaceID()+"'");
317 }
318 token.edge.getPlace().removeToken(token.token);
319 }
320 break;
321 }
322 }
323 }
324
325
326
327
328
329
330 public void updateWriteTokens() throws CapacityException {
331 for (TokenParameter tp : tokens) {
332 switch(tp.scope) {
333 case WRITE:
334 if (tp.edge.getExpression() == null) {
335 boolean newControl = activity == null || activity.getStatus() == Activity.Status.COMPLETED;
336
337 synchronized (tp.edge.getPlace()) {
338 if (tp.token.getControl() == null || tp.token.getControl() != newControl) {
339
340 Token oldToken = tp.token;
341 tp.edge.getPlace().removeToken(oldToken);
342 tp.token = Factory.newToken(newControl);
343 if (logger.isDebugEnabled()) {
344 logger.debug("updateWriteTokens()["+ID+"]: replacing token 'w:"+oldToken.getID() +"' on place '"+tp.edge.getPlaceID()+"' with token 'w"+tp.token.getID()+"'");
345 }
346 addPropertiesToToken(tp.token);
347 tp.edge.getPlace().addToken(tp.token);
348 }
349 }
350 }
351 break;
352 }
353 }
354 }
355
356
357
358
359
360
361 public void putOutputTokens() throws CapacityException, WorkflowFormatException {
362 for (TokenParameter tp : tokens) {
363 switch(tp.scope) {
364 case OUTPUT:
365 if (tp.token == null) {
366 if (tp.edge.getExpression() == null) {
367 if (activity == null) {
368
369 tp.token = Factory.newToken(true, props);
370 } else {
371
372 tp.token = Factory.newToken(activity.getStatus() == Activity.Status.COMPLETED, props);
373 }
374 } else {
375
376 String edgeExpression = tp.edge.getExpression();
377 logger.error("Error: Output token with edge expression '" + edgeExpression + "' for "+toString()+ " is not available! Creating token with SOAP fault instead.");
378 SOAPFault fault = new SOAPFault("env:Receiver", "No Output Available");
379 if (activity != null) {
380 fault.setNode(activity.toString());
381 fault.setDetail("The activity '" +
382 activity +
383 "' has no output parameter related to the edge expression '" +
384 edgeExpression + "'. Please check the edge expression!");
385 } else {
386 fault.setNode("Transition "+transition.getID());
387 fault.setDetail("The transition '" +
388 transition.getID() +
389 "' has no output parameter related to the edge expression '" +
390 edgeExpression + "'. Please check the edge expression!");
391 }
392 tp.token = fault.toToken();
393 }
394 }
395
396 addPropertiesToToken(tp.token);
397
398 if (logger.isDebugEnabled()) {
399 logger.debug("putOutputTokens["+ID+"]: putting token 'o:"+tp.token.getID() +"' to place '"+tp.edge.getPlaceID()+"'");
400 }
401 tp.edge.getPlace().addToken(tp.token);
402 break;
403 }
404 }
405 }
406
407 public void writeWriteTokens() throws CapacityException {
408 logger.warn("WARNING: writeWriteTokens() not yet implemented!");
409
410 }
411
412 public String getID() {
413 return ID;
414 }
415
416 public String getIDwithOutputs() {
417 return IDwithOutputs;
418 }
419
420 public Document getContext() {
421 return context;
422 }
423
424 public SimpleNamespaceContext getNamespaceContext() {
425 return nsContext;
426 }
427
428 public String getNsUri(String nsPrefix) {
429 return nsContext.translateNamespacePrefixToUri(nsPrefix);
430 }
431
432 public ArrayList<TokenParameter> getTokens() {
433 return tokens;
434 }
435
436
437
438
439
440
441
442 public void evaluateXPathEdgeExpressions() throws WorkflowFormatException {
443 if (logger.isDebugEnabled()) {
444 logger.debug("evaluateXPathEdgeExpressions["+ID+"] ...");
445 }
446 TokenProcessor tokenProcessor = new TokenProcessor(nsContext, context, tokens);
447 tokens = tokenProcessor.generateOutputTokens(props);
448 }
449
450 private void updateNamespaceContextFromTransitionProperties() {
451 GenericProperties transProps = transition.getProperties();
452 for (Property prop : transProps.getProperties()) {
453 String key = prop.getKey();
454 if (key.startsWith(Constants.PROP_TRANSITION_XMLNS_)) {
455 String[] strArr = key.split(Constants.PROP_TRANSITION_XMLNS_);
456 if (strArr.length == 2) nsContext.addNamespace(strArr[1],prop.getValue());
457
458 }
459 }
460 }
461
462 private void updatePropertiesFromToken(Token token) {
463 for (Property prop : token.getProperties().getProperties()) {
464 if (props.get(prop.getKey()) == null) {
465 props.addProperty(prop);
466 }
467 }
468 }
469
470
471
472
473
474
475 public void putProperty(String name, String value) {
476 props.put(name,value);
477 }
478
479
480
481
482
483
484
485 private void addPropertiesToToken(Token token) {
486 for (Property prop : props.getProperties()) {
487 String key = prop.getKey();
488 if (key.equals(Constants.PROP_TOKEN_ACTIVITY_MAXATTEMPTS)) continue;
489 if (token.getProperties().get(key) == null) {
490 token.getProperties().addProperty(prop);
491 }
492 }
493 }
494
495
496
497
498
499
500 public static void removeNamespacesWithoutPrefix(Element element) {
501 if (element == null) return;
502 if (element.getNamespacePrefix().length()==0) element.setNamespace(null);
503 List list = element.getChildren();
504 if (list != null) {
505 for (Object child : list) {
506 removeNamespacesWithoutPrefix((Element) child);
507 }
508 }
509 }
510
511
512
513
514
515
516
517
518
519 private void addToContext(Token token, Edge edge) throws WorkflowFormatException {
520 String edgeExpression = edge.getExpression();
521 if (edgeExpression == null) return;
522
523
524 double probability = StringUtils.getProbability(edgeExpression);
525 if (probability > -1) {
526 if (probabilityDistribution == null) probabilityDistribution = new DoubleDistribution();
527 probabilityDistribution.put(probability);
528 }
529 String edgeExpressionWoProbabilities = StringUtils.stripProbability(edgeExpression);
530 if (edgeExpressionWoProbabilities.length() == 0) return;
531
532
533 String[] edgeExpressionElements = StringUtils.stripOwl(edgeExpressionWoProbabilities).split("/");
534
535
536 if (token.getData() == null) {
537 Element dataChild = createTokenElement(edgeExpressionElements[0]);
538 dataChild.setText(token.getControl()?Constants.TRUE:Constants.FALSE);
539 addToContext(edgeExpression,edgeExpressionElements, dataChild);
540 }
541
542
543 else {
544 List children = ((Element) token.getData().get()).cloneContent();
545
546
547
548 if (children != null) {
549 for (Object obj : children) {
550 if (obj instanceof Element) {
551
552 Element dataChild = (Element) obj;
553 dataChild.detach();
554 addToContext(edgeExpression, edgeExpressionElements, dataChild);
555 }
556 }
557 }
558 }
559 }
560
561 private void addToContext(String edgeExpression, String[] edgeExpressionElements, Element dataChild) throws WorkflowFormatException {
562
563 if (edgeExpressionElements.length == 1) {
564
565
566 renameTokenElement(dataChild, edgeExpressionElements[0]);
567 context.getRootElement().addContent(dataChild);
568 }
569
570
571 else {
572
573 Element newDataChild = null;
574 Element parent = null;
575 try {
576 for (int i=0; i<edgeExpressionElements.length-1; i++) {
577
578 Element newelement = createTokenElement(edgeExpressionElements[i]);
579 if (i==0) newDataChild = newelement;
580
581 if (parent != null) parent.addContent(newelement);
582 parent = newelement;
583 }
584 } catch(IllegalNameException e) {
585 throw new WorkflowFormatException("Transition '"+transition.getID()+"' contains edgeExpression '"+edgeExpression+"' that is invalid for renaming input token elements.");
586 }
587
588 String lastEdgeExpressionElement = edgeExpressionElements[edgeExpressionElements.length-1];
589
590 Element lastElement = (Element) dataChild.clone();
591 renameTokenElement(lastElement, lastEdgeExpressionElement);
592 if (parent != null) parent.addContent(lastElement);
593 context.getRootElement().addContent(newDataChild);
594 }
595 }
596
597 private void updateDataGroupProperty(String dataGroup, ArrayList<String> dataGroups) {
598
599 if (dataGroups != null && dataGroups.size() > 0) {
600 StringBuffer str = new StringBuffer();
601 String group = dataGroups.get(0);
602 if (group != null && group.length() > 0) {
603 str.append(group);
604 }
605 for (int i = 1 ; i < dataGroups.size() ; i++) {
606 group = dataGroups.get(i);
607 if (group != null && group.length() > 0) {
608 str.append(" x ");
609 str.append(group);
610 }
611 }
612 props.remove(Constants.PROP_TOKEN_DATA_GROUP);
613 props.put(Constants.PROP_TOKEN_DATA_GROUP, str.toString());
614 } else if (dataGroup != null && dataGroup.length() > 0){
615 props.remove(Constants.PROP_TOKEN_DATA_GROUP);
616 props.put(Constants.PROP_TOKEN_DATA_GROUP, dataGroup);
617 }
618 }
619
620 private void updateResourceAllocationGroupProperty() {
621 String str = transition.getProperties().get(Constants.PROP_TRANSITION_REMOVE_RESOURCE_ALLOCATION_GROUP);
622 if (str != null && !str.equals(Constants.FALSE)) {
623 for (String key : props.keys()) {
624 if (key.startsWith(Constants.PROP_TOKEN_RESOURCE_ALLOCATION_GROUP_)) {
625 props.remove(key);
626 }
627 }
628 }
629 }
630
631
632
633
634 public void generateResourceAllocationGroupPropertyFromActivity() {
635 if (activity == null) return;
636
637 String groupID = transition.getProperties().get(Constants.PROP_TRANSITION_RESOURCE_ALLOCATION_GROUP);
638 if (groupID !=null && activity.getResourceName() != null && activity.getResourceName().length() >0 ) {
639 String resourceUri = activity.getResourceName();
640 if (logger.isDebugEnabled()) {
641 logger.debug("Resource co-allocation: setting property \""+Constants.PROP_TOKEN_RESOURCE_ALLOCATION_GROUP_ + groupID + "\" with value \""+resourceUri+"\" for activity "+activity.getID());
642 }
643 props.put(Constants.PROP_TOKEN_RESOURCE_ALLOCATION_GROUP_ + groupID, resourceUri);
644 }
645 }
646
647 private void renameTokenElement(Element element, String newElementName) throws WorkflowFormatException {
648 String[] ne = newElementName.split(":");
649
650 if (ne.length > 1) {
651
652 String nsPrefix = ne[0];
653
654 String nsUri = getNsUri(nsPrefix);
655 if (nsUri == null) throw new WorkflowFormatException("Transition '"+transition.getID()+"' contains edgeExpression '"+newElementName+"' with namespace prefix '"+nsPrefix+"' which is not defined in the context of the transition. Use <property name=\"xmlns:"+nsPrefix+"\"/> to declare the namespace URI.");
656 element.setName(ne[1]);
657 element.setNamespace(Namespace.getNamespace(nsPrefix, nsUri));
658 } else {
659
660 if (!newElementName.equals("*")) {
661 element.setName(newElementName);
662 element.setNamespace(null);
663 }
664 }
665 }
666
667 private Element createTokenElement(String newElementName) throws WorkflowFormatException, IllegalNameException {
668 String[] ne = newElementName.split(":");
669 if (ne.length > 1) {
670
671 String nsPrefix = ne[0];
672
673 String nsUri = getNsUri(nsPrefix);
674 if (nsUri == null) throw new WorkflowFormatException("Transition '"+transition.getID()+"' contains edgeExpression '"+newElementName+"' with namespace prefix '"+nsPrefix+"' which is not defined in the context of the transition. Use <property name=\"xmlns:"+nsPrefix+"\"/> to declare the namespace URI.");
675 return new Element(ne[1], Namespace.getNamespace(nsPrefix, nsUri));
676 } else {
677
678 return new Element(ne[0]);
679 }
680 }
681
682
683
684
685
686
687 public void movePropertyToInputTokens(String propertyName) {
688 String value = props.get(propertyName);
689 if (value != null) {
690 loop: for (TokenParameter token : tokens) {
691 switch (token.scope) {
692 case READ:
693 continue loop;
694 case INPUT:
695 token.token.getProperties().put(propertyName,value);
696 break;
697 case WRITE:
698 case OUTPUT:
699 break loop;
700 }
701 }
702 }
703 }
704
705 public GenericProperties getProps() {
706 return props;
707 }
708
709 public Activity getActivity() {
710 return activity;
711 }
712
713 public void setActivity(Activity activity) {
714 this.activity = activity;
715 }
716
717 public DoubleDistribution getProbabilityDistribution() {
718 return probabilityDistribution;
719 }
720
721
722
723
724
725
726 public String updateID() {
727 StringBuffer buffer = new StringBuffer(ID);
728 for (TokenParameter token : tokens) {
729 switch (token.scope) {
730 case OUTPUT:
731 if (token.token != null) {
732 buffer.append(",o:").append(token.token.getID());
733 }
734 break;
735 }
736 }
737 IDwithOutputs = buffer.toString();
738 return IDwithOutputs;
739 }
740
741 public String toString() {
742 StringBuffer buffer = new StringBuffer("TransitionOccurrence[");
743 buffer.append(transition.getID());
744 for (TokenParameter token : tokens) {
745 switch (token.scope) {
746 case READ:
747 buffer.append(", r:");
748 break;
749 case INPUT:
750 buffer.append(", i:");
751 break;
752 case WRITE:
753 buffer.append(", w:");
754 break;
755 case OUTPUT:
756 buffer.append(", o:");
757 break;
758 }
759 if (token.token != null) buffer.append(token.token.getID());
760 }
761 buffer.append(']');
762 return buffer.toString();
763 }
764
765 public static class DefaultNamespaceContext extends SimpleNamespaceContext {
766 public DefaultNamespaceContext() {
767 super();
768 addNamespace("tns","http://target.namespace");
769 addNamespace("gwdl", "http://www.gridworkflow.org/gworkflowdl");
770 addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
771 addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
772 addNamespace("soapenv", "http://www.w3.org/2003/05/soap-envelope");
773
774 }
775 }
776
777 }