001: /*
002: * The contents of this file are subject to the terms of the Common Development
003: * and Distribution License (the License). You may not use this file except in
004: * compliance with the License.
005: *
006: * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
007: * or http://www.netbeans.org/cddl.txt.
008: *
009: * When distributing Covered Code, include this CDDL Header Notice in each file
010: * and include the License file at http://www.netbeans.org/cddl.txt.
011: * If applicable, add the following below the CDDL Header, with the fields
012: * enclosed by brackets [] replaced by your own identifying information:
013: * "Portions Copyrighted [year] [name of copyright owner]"
014: *
015: * The Original Software is NetBeans. The Initial Developer of the Original
016: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
017: * Microsystems, Inc. All Rights Reserved.
018: */
019:
020: package org.netbeans.modules.bpel.debugger.bdiclient.impl;
021:
022: import java.lang.ref.WeakReference;
023: import java.util.ArrayList;
024: import java.util.Collections;
025: import java.util.LinkedList;
026: import java.util.List;
027: import java.util.concurrent.atomic.AtomicInteger;
028: import javax.xml.namespace.QName;
029: import org.netbeans.api.debugger.Breakpoint;
030: import org.netbeans.api.debugger.DebuggerManager;
031: import org.netbeans.modules.bpel.debugger.api.BpelDebugger;
032: import org.netbeans.modules.bpel.debugger.api.EvaluationException;
033: import org.netbeans.modules.bpel.debugger.api.InvalidStateException;
034: import org.netbeans.modules.bpel.debugger.api.ProcessInstance;
035: import org.netbeans.modules.bpel.debugger.api.breakpoints.BpelFaultBreakpoint;
036: import org.netbeans.modules.bpel.debugger.api.variables.Value;
037: import org.netbeans.modules.bpel.debugger.api.variables.Variable;
038: import org.netbeans.modules.bpel.debugger.eventlog.ActivityCompletedRecord;
039: import org.netbeans.modules.bpel.debugger.eventlog.ActivityStartedRecord;
040: import org.netbeans.modules.bpel.debugger.eventlog.BranchCompletedRecord;
041: import org.netbeans.modules.bpel.debugger.eventlog.BranchStartedRecord;
042: import org.netbeans.modules.bpel.debugger.eventlog.EventLog;
043: import org.netbeans.modules.bpel.debugger.eventlog.ProcessInstanceCompletedRecord;
044: import org.netbeans.modules.bpel.debugger.eventlog.ProcessInstanceStartedRecord;
045: import org.netbeans.modules.bpel.debugger.pem.ProcessExecutionModelImpl;
046: import org.netbeans.modules.bpel.debugger.variables.SimpleValueImpl;
047: import org.netbeans.modules.bpel.debugger.variables.SimpleVariableImpl;
048: import org.netbeans.modules.bpel.debugger.variables.Util;
049: import org.netbeans.modules.bpel.debugger.variables.WsdlMessageVariableImpl;
050: import org.netbeans.modules.bpel.debugger.variables.XmlElementValueImpl;
051: import org.netbeans.modules.bpel.debugger.variables.XmlElementVariableImpl;
052: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.BPELProcessInstanceRef;
053: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.BPELVariable;
054: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.DebugFrame;
055: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.DebuggableEngine;
056: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.VirtualBPELEngine;
057: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.XpathExpressionException;
058: import org.netbeans.modules.bpel.debugger.BpelDebuggerImpl;
059: import org.netbeans.modules.bpel.debugger.BreakPosition;
060: import org.netbeans.modules.bpel.debugger.api.CorrelationSet;
061: import org.netbeans.modules.bpel.debugger.api.Fault;
062: import org.netbeans.modules.bpel.debugger.api.RuntimePartnerLink;
063: import org.netbeans.modules.bpel.debugger.api.pem.ProcessExecutionModel.Branch;
064: import org.netbeans.modules.bpel.debugger.eventlog.ActivityTerminatedRecord;
065: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.BPELPartnerLink;
066: import org.netbeans.modules.bpel.debuggerbdi.rmi.api.BPELProcessRef;
067: import org.w3c.dom.Element;
068:
069: /**
070: *
071: * @author Alexander Zgursky
072: * @author Kirill Sorokin
073: */
074: public class ProcessInstanceImpl implements ProcessInstance {
075:
076: private final String myId;
077: private final BpelProcessImpl myBpelProcess;
078: private final ProcessInstancesModelImpl myModel;
079: private final BPELProcessInstanceRef myProcessInstanceRef;
080:
081: private BreakPosition myBreakPosition;
082: private BreakPosition myLastPosition;
083: private Object myWaitLock = new Object();
084: private Object mySyncLock = new Object();
085: private AtomicInteger myState = new AtomicInteger(STATE_RUNNING);
086:
087: private boolean myIsUndeployed;
088: private boolean myStepIntoFlag;
089: private boolean myStepOverFlag;
090: private boolean myStepOutFlag;
091: private boolean myPauseFlag;
092: private final List<BDIDebugFrame> myFrames = Collections
093: .synchronizedList(new ArrayList<BDIDebugFrame>());
094: private EventLog myEventLog;
095: private WeakReference<ProcessExecutionModelImpl> myPemRef;
096:
097: private List<Fault> myFaults = new LinkedList<Fault>();
098:
099: /** Creates a new instance of ProcessInstanceImpl */
100: public ProcessInstanceImpl(final ProcessInstancesModelImpl model,
101: final BPELProcessInstanceRef processInstanceRef) {
102:
103: myModel = model;
104: myProcessInstanceRef = processInstanceRef;
105: myBpelProcess = myModel.getProcess(myProcessInstanceRef
106: .template());
107: myId = processInstanceRef.globalID();
108: }
109:
110: public String getName() {
111: return myBpelProcess.getName() + " #" + myId;
112: }
113:
114: public int getState() {
115: return myState.get();
116: }
117:
118: public BreakPosition getCurrentPosition() {
119: return myBreakPosition;
120: }
121:
122: public void pause() {
123: myPauseFlag = true;
124: }
125:
126: public void resume() {
127: synchronized (myWaitLock) {
128: myWaitLock.notifyAll();
129: }
130: }
131:
132: public void stepInto() {
133: myStepIntoFlag = true;
134: resume();
135: }
136:
137: public void stepOver() {
138: myStepOverFlag = true;
139: myLastPosition = myBreakPosition;
140:
141: resume();
142: }
143:
144: public void stepOut() {
145: myStepOutFlag = true;
146: myLastPosition = myBreakPosition;
147:
148: resume();
149: }
150:
151: public void terminate() {
152: final BDIDebugger debugger = getDebugger().getBDIDebugger();
153:
154: if (debugger != null) {
155: final VirtualBPELEngine engine = debugger
156: .getVirtualBPELEngine();
157:
158: if (engine != null) {
159: engine.terminatePI(myProcessInstanceRef.globalGUID());
160: }
161: }
162: }
163:
164: public String getId() {
165: return myId;
166: }
167:
168: public BpelProcessImpl getProcess() {
169: return myBpelProcess;
170: }
171:
172: public QName getProcessQName() {
173: return myBpelProcess.getQName();
174: }
175:
176: public Variable[] getVariables() {
177: final BreakPosition position = myBreakPosition;
178:
179: if (position != null) {
180: final BDIDebugFrame frame = position.getFrame();
181: final DebuggableEngine engine = frame.getDebuggableEngine();
182:
183: if (engine != null) {
184: final String[] names = engine.getVariables();
185: final Variable[] variables = new Variable[names.length];
186:
187: for (int i = 0; i < names.length; i++) {
188: final String name = names[i];
189: final BPELVariable engineVar = engine
190: .getVariable(name);
191:
192: if (engineVar == null) {
193: continue;
194: }
195:
196: if (engineVar.isWSDLMessage()) {
197: variables[i] = new WsdlMessageVariableImpl(
198: name, position, engineVar);
199: } else if (engineVar.isSimpleType()) {
200: variables[i] = new SimpleVariableImpl(name,
201: position, engineVar);
202: } else {
203: variables[i] = new XmlElementVariableImpl(name,
204: position, engineVar);
205: }
206: }
207:
208: return variables;
209: }
210: }
211:
212: return new Variable[0];
213: }
214:
215: public RuntimePartnerLink[] getRuntimePartnerLinks() {
216: final BreakPosition position = myBreakPosition;
217:
218: if (position != null) {
219: final BDIDebugFrame frame = position.getFrame();
220: final DebuggableEngine engine = frame.getDebuggableEngine();
221:
222: if (engine != null) {
223: final String[] names = engine.getPartnerLinks();
224: final RuntimePartnerLink[] pLinks = new RuntimePartnerLink[names.length];
225:
226: for (int i = 0; i < names.length; i++) {
227: final BPELPartnerLink enginePLink = engine
228: .getPartnerLink(names[i]);
229:
230: if (enginePLink == null) {
231: continue;
232: }
233:
234: pLinks[i] = new RuntimePartnerLinkImpl(enginePLink);
235: }
236:
237: return pLinks;
238: }
239: }
240:
241: return new RuntimePartnerLink[0];
242: }
243:
244: public CorrelationSet[] getCorrelationSets() {
245: final List<CorrelationSet> list = new LinkedList<CorrelationSet>();
246:
247: final BPELProcessRef processRef = myBpelProcess.getProcessRef();
248:
249: for (String name : processRef.allCorrelationSetsNames()) {
250: final String value = myProcessInstanceRef
251: .getCorrelationSetValue(name);
252:
253: if (value == null) {
254: list.add(new CorrelationSetImpl(name, processRef
255: .getCorrelationSetId(name), null, new QName[0],
256: new QName[0], new String[0]));
257: } else {
258: String[] names = myProcessInstanceRef
259: .getCorrelationSetPropertyNames(name);
260: String[] types = myProcessInstanceRef
261: .getCorrelationSetPropertyTypes(name);
262: String[] values = myProcessInstanceRef
263: .getCorrelationSetPropertyValues(name);
264:
265: QName[] realNames = new QName[names.length];
266: QName[] realTypes = new QName[names.length];
267:
268: for (int i = 0; i < names.length; i++) {
269: String[] temp;
270:
271: temp = names[i].split("\n");
272: realNames[i] = new QName(temp[0], temp[2], temp[1]);
273:
274: temp = types[i].split("\n");
275: realTypes[i] = new QName(temp[0], temp[2], temp[1]);
276: }
277:
278: list.add(new CorrelationSetImpl(name, processRef
279: .getCorrelationSetId(name), value, realNames,
280: realTypes, values));
281: }
282: }
283:
284: return list.toArray(new CorrelationSet[list.size()]);
285: }
286:
287: public Fault[] getFaults() {
288: return myFaults.toArray(new Fault[myFaults.size()]);
289: }
290:
291: public Value evaluate(final String expression)
292: throws InvalidStateException, EvaluationException {
293:
294: if (expression == null || expression.trim().equals("")) {
295: return null;
296: }
297:
298: final BreakPosition position = myBreakPosition;
299: if (position != null) {
300: final BDIDebugFrame frame = position.getFrame();
301: final DebuggableEngine engine = frame.getDebuggableEngine();
302:
303: if (engine != null) {
304: try {
305: final String string = engine.evaluate(expression);
306:
307: if (string != null) {
308: final Element element = Util
309: .parseXmlElement(string);
310:
311: if (element != null) {
312: return new XmlElementValueImpl(element);
313: } else {
314: return new SimpleValueImpl(string);
315: }
316: }
317: } catch (XpathExpressionException ex) {
318: //TODO:change to NLS
319: throw new EvaluationException(
320: "Can not evaluate the expression", ex);
321: }
322: }
323: } else {
324: //TODO:change to NLS
325: throw new InvalidStateException(
326: "Process instance is not suspended");
327: }
328:
329: return null;
330: }
331:
332: public EventLog getEventLog() {
333: return myEventLog;
334: }
335:
336: public ProcessExecutionModelImpl getProcessExecutionModel() {
337: ProcessExecutionModelImpl pem = null;
338: if (myPemRef != null) {
339: pem = myPemRef.get();
340: }
341:
342: if (pem == null) {
343: pem = ProcessExecutionModelImpl.build(this );
344: myPemRef = new WeakReference<ProcessExecutionModelImpl>(pem);
345: }
346:
347: return pem;
348: }
349:
350: // Protected ///////////////////////////////////////////////////////////////
351: protected void setUndeployed(final boolean isUndeployed) {
352:
353: myIsUndeployed = isUndeployed;
354: }
355:
356: protected void onActivityStarted(final BreakPosition position) {
357:
358: //TODO:why do we need this check?
359: if (getDebugger().getState() != BpelDebugger.STATE_RUNNING) {
360: return;
361: }
362:
363: if (myIsUndeployed) {
364: return;
365: }
366:
367: synchronized (mySyncLock) {
368: if (myEventLog != null) {
369: myEventLog.addRecord(new ActivityStartedRecord(position
370: .getFrame().getId(), position.getXpath()));
371: }
372:
373: final String path = getDebugger().getSourcePath()
374: .getSourcePath(position.getProcessQName());
375: if (path == null) {
376: return;
377: }
378:
379: if (myPauseFlag
380: || stepIntoSatisfied(position)
381: || stepOverSatisfied(position)
382: || stepOutSatisfied(position)
383: || getDebugger().hasBreakpoint(path,
384: position.getXpath())) {
385:
386: myPauseFlag = false;
387: myStepIntoFlag = false;
388: myStepOverFlag = false;
389: myStepOutFlag = false;
390: getDebugger().clearRunToCursorBreakpoint();
391:
392: getProcessExecutionModel()
393: .setCurrentBranchWithoutResume(
394: position.getBranchId());
395:
396: myBreakPosition = position;
397: setState(STATE_SUSPENDED);
398: doWait();
399: myBreakPosition = null;
400: setState(STATE_RUNNING);
401: } else {
402: // This call mught seem redundant, but it triggers a bunch of
403: // important updates, like BpelDebugger.setCurrentPosition(null)
404: // which causes the annotations to be redrawn.
405: setState(STATE_RUNNING);
406: }
407: }
408: }
409:
410: protected void onActivityCompleted(final BreakPosition position) {
411:
412: if (myEventLog != null) {
413: myEventLog.addRecord(new ActivityCompletedRecord(position
414: .getFrame().getId(), position.getXpath()));
415: }
416: }
417:
418: protected void onFault(final BreakPosition position,
419: final String strFaultQName, final BPELVariable faultData) {
420:
421: //TODO:why do we need this check?
422: if (getDebugger().getState() != BpelDebugger.STATE_RUNNING) {
423: return;
424: }
425:
426: if (myIsUndeployed) {
427: return;
428: }
429:
430: synchronized (mySyncLock) {
431: final QName faultQName;
432: if ((strFaultQName == null)
433: || strFaultQName.trim().equals("")) {
434: faultQName = null;
435: } else {
436: final String[] temp = strFaultQName.split("\n");
437:
438: faultQName = new QName(temp[0], temp[2], temp[1]);
439: }
440:
441: // add the fault to the list, so it can be displayed by the
442: // processes view
443: if (faultData == null) {
444: myFaults.add(new FaultImpl(faultQName, position
445: .getXpath(), null));
446: } else if (faultData.isWSDLMessage()) {
447: myFaults.add(new FaultImpl(faultQName, position
448: .getXpath(), new WsdlMessageVariableImpl(
449: "faultData", position, faultData)));
450: } else if (faultData.isSimpleType()) {
451: myFaults.add(new FaultImpl(faultQName, position
452: .getXpath(), new SimpleVariableImpl(
453: "faultData", position, faultData)));
454: } else {
455: myFaults.add(new FaultImpl(faultQName, position
456: .getXpath(), new XmlElementVariableImpl(
457: "faultData", position, faultData)));
458: }
459:
460: final String path = getDebugger().getSourcePath()
461: .getSourcePath(position.getProcessQName());
462: if (path == null) {
463: return;
464: }
465:
466: //TODO:search for Fault Breakpoint is inconsistent with
467: //search for Line (Activity) breakpoint - the later uses NB
468: //breakpoints
469: final Breakpoint[] nbBreakpoints = DebuggerManager
470: .getDebuggerManager().getBreakpoints();
471:
472: boolean foundfb = false;
473: for (Breakpoint nbBreakpoint : nbBreakpoints) {
474: if (nbBreakpoint instanceof BpelFaultBreakpoint) {
475: final BpelFaultBreakpoint fb = (BpelFaultBreakpoint) nbBreakpoint;
476:
477: if (!nbBreakpoint.isEnabled()) {
478: continue;
479: }
480:
481: if (fb.getProcessQName().equals(
482: position.getProcessQName())) {
483: if (fb.getFaultQName() == null) {
484: foundfb = true;
485: break;
486: } else if (fb.getFaultQName()
487: .equals(faultQName)) {
488: foundfb = true;
489: break;
490: }
491: }
492: }
493: }
494:
495: if (foundfb) {
496: myStepIntoFlag = false;
497: getDebugger().clearRunToCursorBreakpoint();
498: myBreakPosition = position;
499: setState(STATE_SUSPENDED);
500: doWait();
501: myBreakPosition = null;
502: setState(STATE_RUNNING);
503: }
504: }
505: }
506:
507: protected void onXpathException(final DebugFrame frame) {
508:
509: if (getRootFrame() == frame) {
510: setState(STATE_FAILED);
511: }
512: }
513:
514: protected void onProcessInstanceStarted() {
515: myEventLog = new EventLog();
516: myEventLog.addRecord(new ProcessInstanceStartedRecord());
517: }
518:
519: protected void onProcessInstanceDied() {
520: setState(STATE_COMPLETED);
521:
522: if (myEventLog != null) {
523: myEventLog.addRecord(new ProcessInstanceCompletedRecord());
524: }
525:
526: myModel.processInstanceCompleted(this );
527: }
528:
529: protected void onTerminate(final BreakPosition position) {
530: if (myEventLog != null) {
531: myEventLog.addRecord(new ActivityTerminatedRecord(position
532: .getFrame().getId(), position.getXpath()));
533: }
534: }
535:
536: protected void onExit(final BDIDebugFrame frame) {
537:
538: if (myEventLog != null) {
539: myEventLog.addRecord(new BranchCompletedRecord(frame
540: .getId()));
541: }
542: }
543:
544: protected BDIDebugFrame addFrame(final String id,
545: final String parentFrameId) {
546: final BDIDebugFrame frame = new BDIDebugFrame(this , id);
547:
548: myFrames.add(frame);
549:
550: if (myEventLog != null) {
551: myEventLog.addRecord(new BranchStartedRecord(id,
552: parentFrameId));
553: }
554:
555: return frame;
556: }
557:
558: // Private /////////////////////////////////////////////////////////////////
559: private void setState(final int newState) {
560: int oldState = myState.getAndSet(newState);
561: myModel.processInstanceStateChanged(this , oldState, newState);
562: }
563:
564: private void doWait() {
565: try {
566: synchronized (myWaitLock) {
567: //TODO:Always put wait() in a condition loop
568: //since wait() can sometimes stop waiting for some reason - see
569: //wait() javadoc.
570: myWaitLock.wait();
571: }
572: } catch (InterruptedException ie) {
573: // process interrupted
574: }
575: }
576:
577: private boolean stepIntoSatisfied(final BreakPosition position) {
578: if (!myStepIntoFlag) {
579: return false;
580: }
581:
582: // If the current branch is not that of the position -- we should not
583: // stop
584: final Branch branch = getProcessExecutionModel()
585: .getCurrentBranch();
586: if ((branch == null)
587: || !position.getBranchId().equals(branch.getId())) {
588: return false;
589: }
590:
591: return true;
592: }
593:
594: private boolean stepOverSatisfied(final BreakPosition position) {
595: if (!myStepOverFlag) {
596: return false;
597: }
598:
599: // If the current branch is not that of the position -- we should not
600: // stop
601: final Branch branch = getProcessExecutionModel()
602: .getCurrentBranch();
603: if ((branch == null)
604: || !position.getBranchId().equals(branch.getId())) {
605: return false;
606: }
607:
608: final String lastXpath = myLastPosition.getXpath();
609: final String currentXpath = position.getXpath();
610:
611: // We stop after step over if the current element is not "inside" the
612: // last element
613: if (!currentXpath.startsWith(lastXpath)
614: || currentXpath.equals(lastXpath)) {
615: return true;
616: }
617:
618: return false;
619: }
620:
621: private boolean stepOutSatisfied(final BreakPosition position) {
622: if (!myStepOutFlag) {
623: return false;
624: }
625:
626: // If the current branch is not that of the position -- we should not
627: // stop
628: final Branch branch = getProcessExecutionModel()
629: .getCurrentBranch();
630: if ((branch == null)
631: || !position.getBranchId().equals(branch.getId())) {
632: return false;
633: }
634:
635: final String lastXpath = myLastPosition.getXpath();
636: final String currentXpath = position.getXpath();
637:
638: final String lastXpathParent = lastXpath.substring(0, lastXpath
639: .lastIndexOf("/"));
640:
641: // We stop after step out if the current element "contains" the last
642: // one
643: if (!currentXpath.startsWith(lastXpathParent)) {
644: return true;
645: }
646:
647: return false;
648: }
649:
650: //TODO:get rid of the notion of "root frame", since there may be more
651: //than one "root frame" e.g. main activity frame and event frame
652: private BDIDebugFrame getRootFrame() {
653: return myFrames.get(0);
654: }
655:
656: private BpelDebuggerImpl getDebugger() {
657: return myModel.getDebugger();
658: }
659: }
|