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;
021:
022: import java.io.File;
023: import java.lang.reflect.UndeclaredThrowableException;
024: import java.util.Collections;
025: import java.util.IdentityHashMap;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.concurrent.atomic.AtomicReference;
029: import java.util.logging.Logger;
030:
031: import org.netbeans.api.debugger.Breakpoint;
032: import org.netbeans.api.debugger.DebuggerManager;
033: import org.netbeans.api.debugger.Session;
034: import org.netbeans.modules.bpel.debugger.api.BpelDebugger;
035: import org.netbeans.modules.bpel.debugger.api.DebugException;
036: import org.netbeans.modules.bpel.debugger.api.ProcessInstance;
037: import org.netbeans.modules.bpel.debugger.api.ProcessInstancesModel;
038: import org.netbeans.modules.bpel.debugger.api.SourcePath;
039: import org.netbeans.modules.bpel.debugger.api.breakpoints.BpelBreakpoint;
040: import org.netbeans.modules.bpel.debugger.api.breakpoints.LineBreakpoint;
041: import org.netbeans.modules.bpel.debugger.bdiclient.impl.BDIDebugConnector;
042: import org.netbeans.modules.bpel.debugger.bdiclient.impl.BDIDebugger;
043: import org.netbeans.modules.bpel.debugger.bdiclient.impl.ProcessInstanceImpl;
044: import org.netbeans.modules.bpel.debugger.bdiclient.impl.ProcessInstancesModelImpl;
045: import org.netbeans.modules.bpel.debugger.breakpoints.SBYNActivityBreakpoint;
046: import org.netbeans.modules.bpel.debugger.breakpoints.SBYNBreakpoint;
047: import org.netbeans.spi.debugger.ContextProvider;
048: import org.netbeans.spi.debugger.DebuggerEngineProvider;
049: import org.openide.util.NbBundle;
050:
051: /**
052: * This class provides the central debugging control for the BPEL debugger.
053: * It delegates to a DebugEngineConnector for the implementation of how
054: * a particular BPEL debugger is actually controlled.
055: *
056: * @author Sun Microsystems
057: */
058: public class BpelDebuggerImpl extends BpelDebugger {
059:
060: private static Logger LOGGER = Logger
061: .getLogger(BpelDebuggerImpl.class.getName());
062:
063: private Thread mStartingThread;
064: protected BDIDebugConnector mConnector;
065: private int mState = STATE_DISCONNECTED;
066: private BpelDebuggerEngineProvider myDebuggerEngineProvider;
067: private Session mSession;
068: private AtomicReference<BreakPosition> mCurrentPositionRef = new AtomicReference<BreakPosition>();
069: private AtomicReference<ProcessInstanceImpl> mCurrentProcessInstanceRef = new AtomicReference<ProcessInstanceImpl>();
070:
071: private Map myBreakpointsMap = Collections
072: .synchronizedMap(new IdentityHashMap());
073:
074: private SourcePath mSourcePath;
075: private ProcessInstancesModelImpl mProcessInstancesModel;
076: private ProcessInstancesModelListener mProcessInstancesModelListener;
077: private DebugException mException;
078: private SBYNActivityBreakpoint mRunToCursorBreakpoint;
079:
080: public BpelDebuggerImpl(ContextProvider lookupProvider) {
081: super (lookupProvider);
082:
083: //TODO:why not just call lookupFirst?
084: List<? extends DebuggerEngineProvider> l = lookupProvider
085: .lookup(null, DebuggerEngineProvider.class);
086: int i;
087: int k = l.size();
088:
089: for (i = 0; i < k; i++) {
090: if (l.get(i) instanceof BpelDebuggerEngineProvider) {
091: myDebuggerEngineProvider = (BpelDebuggerEngineProvider) l
092: .get(i);
093: }
094: }
095:
096: if (myDebuggerEngineProvider == null) {
097: throw new IllegalArgumentException(
098: "BpelDebuggerEngineProvider have to be used " + // NOI18N
099: "to start BpelDebugger!"); // NOI18N
100: }
101:
102: // myDebuggerEngineProvider = (DebuggerEngineProvider)lookupProvider.lookupFirst(null, DebuggerEngineProvider.class);
103: mSession = myDebuggerEngineProvider.getSession();
104: mProcessInstancesModel = new ProcessInstancesModelImpl(this );
105: mProcessInstancesModelListener = new ProcessInstancesModelListener();
106: mProcessInstancesModel
107: .addListener(mProcessInstancesModelListener);
108: }
109:
110: public SourcePath getSourcePath() {
111:
112: if (mSourcePath == null) {
113: mSourcePath = getLookupProvider().lookupFirst(null,
114: SourcePath.class);
115: }
116: return mSourcePath;
117: }
118:
119: /**
120: * It should be called from an action as a part of a session temination.
121: * Don't call it from anywhere except KillActionProvider.
122: * Use Session.kill() instead.
123: */
124: public void finish() {
125: int oldState = getState();
126: if (oldState == STATE_DISCONNECTED) {
127: return;
128: }
129: setState(STATE_DISCONNECTED);
130:
131: mProcessInstancesModel
132: .removeListener(mProcessInstancesModelListener);
133: for (ProcessInstanceImpl processInstance : mProcessInstancesModel
134: .getProcessInstances()) {
135: if (processInstance.getState() == ProcessInstance.STATE_SUSPENDED) {
136: processInstance.resume();
137: }
138: }
139: //Just a back-up
140: mProcessInstancesModel.clear();
141:
142: if (mStartingThread != null) {
143: mStartingThread.interrupt();
144: mStartingThread = null;
145: }
146:
147: Exception disconnectionException = null;
148: if (mConnector != null) {
149: try {
150: mConnector.detach();
151: } catch (Exception e) {
152: disconnectionException = e;
153: }
154: }
155:
156: if (oldState == BpelDebugger.STATE_STARTING) {
157: if (getException() != null) {
158: traceDebugException(NbBundle.getMessage(
159: BpelDebuggerImpl.class,
160: "ERR_UnableToStartSession"), getException());
161: } else {
162: getTracer().println(
163: NbBundle.getMessage(BpelDebuggerImpl.class,
164: "MSG_StopConnecting"));
165: }
166: } else {
167: if (getException() != null) {
168: traceDebugException(NbBundle
169: .getMessage(BpelDebuggerImpl.class,
170: "ERR_SessionTerminated"),
171: getException());
172: } else {
173: getTracer().println(
174: NbBundle.getMessage(BpelDebuggerImpl.class,
175: "MSG_SessionFinished"));
176: }
177: }
178:
179: if (disconnectionException != null) {
180: traceDebugException(NbBundle.getMessage(
181: BpelDebuggerImpl.class,
182: "ERR_ErrorWhileDisconnecting"),
183: disconnectionException);
184: }
185:
186: if (myDebuggerEngineProvider.getDestructor() != null) {
187: myDebuggerEngineProvider.getDestructor().killEngine();
188: }
189: }
190:
191: public void setException(DebugException e) {
192: if (getState() == STATE_DISCONNECTED) {
193: return;
194: }
195:
196: mException = e;
197: mSession.kill();
198: }
199:
200: private void setState(int state) {
201: if (state == mState) {
202: return;
203: }
204: int oldState = mState;
205: mState = state;
206: firePropertyChange(PROP_STATE, new Integer(oldState),
207: new Integer(state));
208: }
209:
210: public int getState() {
211: return mState;
212: }
213:
214: public void setStartingThread(Thread startingThread) {
215: mStartingThread = startingThread;
216: setState(STATE_STARTING);
217: }
218:
219: public void unsetStartingThread() {
220: mStartingThread = null;
221: }
222:
223: public void setRunning(String host, int port) {
224: if (BDIDebugConnector.getDebugConnector(host, port) != null) {
225: setException(new DebugException(NbBundle.getMessage(
226: BpelDebuggerImpl.class, "ERR_AlreadyConnected",
227: host, "" + port)));
228: return;
229: }
230:
231: getTracer().println(
232: NbBundle.getMessage(BpelDebuggerImpl.class,
233: "MSG_Connecting", host, "" + port));
234: mConnector = new BDIDebugConnector(this );
235: if (!mConnector.isInitialized()) {
236: setException(new DebugException(mConnector.getException()));
237: return;
238: }
239:
240: mConnector.attach(host, port);
241: if (!mConnector.isAttached()) {
242: setException(new DebugException(NbBundle.getMessage(
243: BpelDebuggerImpl.class, "ERR_UnableToConnect",
244: host, "" + port), mConnector.getException()));
245: return;
246: }
247:
248: synchronizeBreakpoints();
249:
250: getTracer().println(
251: NbBundle.getMessage(BpelDebuggerImpl.class,
252: "MSG_SessionStarted"));
253: setState(STATE_RUNNING);
254: }
255:
256: public ProcessInstancesModelImpl getProcessInstancesModel() {
257: return mProcessInstancesModel;
258: }
259:
260: public DebugException getException() {
261: return mException;
262: }
263:
264: public BDIDebugger getBDIDebugger() {
265: if (mConnector != null) {
266: return mConnector.getBDIDebugger();
267: } else {
268: return null;
269: }
270: }
271:
272: /**
273: * Registers added breakpoint at the target BPEL engine.<br>
274: * This method would normally be called from
275: * <code>DebuggerManagerListener.breakpointAdded()</code> event handler.
276: *
277: * @param breakpoint breakpoint that has been added
278: *
279: * @see #breakpointRemoved
280: * @see #getBreakpoints
281: */
282: public void breakpointAdded(BpelBreakpoint breakpoint) {
283: if (breakpoint instanceof LineBreakpoint) {
284: myBreakpointsMap.put(breakpoint,
285: new SBYNActivityBreakpoint(
286: (LineBreakpoint) breakpoint, this ));
287: }
288: }
289:
290: /**
291: * Removes registered breakpoint from the target BPEL engine.<br>
292: * This method would normally be called from
293: * <code>DebuggerManagerListener.breakpointRemoved()</code> event handler.
294: *
295: * @param breakpoint breakpoint that has been removed
296: *
297: * @see #breakpointAdded
298: * @see #getBreakpoints
299: */
300: public void breakpointRemoved(BpelBreakpoint breakpoint) {
301: myBreakpointsMap.remove(breakpoint);
302: }
303:
304: public boolean hasBreakpoint(String path, String xpath) {
305: SBYNBreakpoint[] bps = getBreakpoints();
306: for (SBYNBreakpoint bp : bps) {
307: if (bp.isEnabled()
308: && new File(path).equals(new File(bp.getURL()))
309: && xpath.equals(bp.getXpath())) {
310: return true;
311: }
312: }
313:
314: if (mRunToCursorBreakpoint != null
315: && new File(path).equals(new File(
316: mRunToCursorBreakpoint.getURL()))
317: && xpath.equals(mRunToCursorBreakpoint.getXpath())) {
318: return true;
319: }
320:
321: return false;
322: }
323:
324: private void synchronizeBreakpoints() {
325: Breakpoint[] nbBreakpoints = DebuggerManager
326: .getDebuggerManager().getBreakpoints();
327:
328: for (Breakpoint nbBreakpoint : nbBreakpoints) {
329: if (nbBreakpoint instanceof LineBreakpoint) {
330: LineBreakpoint lbp = (LineBreakpoint) nbBreakpoint;
331: myBreakpointsMap.put(lbp, new SBYNActivityBreakpoint(
332: lbp, this ));
333: }
334: }
335: }
336:
337: private void activateSession() {
338: if (mSession != DebuggerManager.getDebuggerManager()
339: .getCurrentSession()) {
340: DebuggerManager.getDebuggerManager().setCurrentSession(
341: mSession);
342: }
343: }
344:
345: /**
346: * Returns breakpoints that have been registered at the target BPEL engine.
347: *
348: * @return breakpoints that have been registered at the target BPEL engine
349: *
350: * @see #breakpointAdded
351: * @see #breakpointRemoved
352: */
353: public SBYNBreakpoint[] getBreakpoints() {
354: synchronized (myBreakpointsMap) {
355: return (SBYNBreakpoint[]) myBreakpointsMap
356: .values()
357: .toArray(
358: new SBYNBreakpoint[myBreakpointsMap.size()]);
359: }
360: }
361:
362: private void setCurrentPosition(BreakPosition newPosition) {
363: BreakPosition oldPosition = mCurrentPositionRef
364: .getAndSet(newPosition);
365:
366: firePropertyChange(PROP_CURRENT_POSITION, oldPosition,
367: newPosition);
368:
369: if (newPosition != null) {
370: activateSession();
371: }
372: }
373:
374: // BpelDebugger interface methods
375: // BpelDebugger interface methods
376: // BpelDebugger interface methods
377:
378: public void stepInto() {
379: final ProcessInstanceImpl processInstance = getCurrentProcessInstance();
380:
381: if (processInstance != null) {
382: processInstance.stepInto();
383: }
384: }
385:
386: public void stepOver() {
387: final ProcessInstanceImpl processInstance = getCurrentProcessInstance();
388:
389: if (processInstance != null) {
390: processInstance.stepOver();
391: }
392: }
393:
394: public void stepOut() {
395: final ProcessInstanceImpl processInstance = getCurrentProcessInstance();
396:
397: if (processInstance != null) {
398: processInstance.stepOut();
399: }
400: }
401:
402: public void pause() {
403: for (ProcessInstanceImpl instance : mProcessInstancesModel
404: .getProcessInstances()) {
405:
406: instance.pause();
407: }
408: }
409:
410: public void resume() {
411: final ProcessInstanceImpl processInstance = getCurrentProcessInstance();
412:
413: if (processInstance != null) {
414: processInstance.resume();
415: }
416:
417: setCurrentPosition(null);
418: }
419:
420: public BreakPosition getCurrentPosition() {
421: return mCurrentPositionRef.get();
422: }
423:
424: public ProcessInstanceImpl getCurrentProcessInstance() {
425: return mCurrentProcessInstanceRef.get();
426: }
427:
428: public int getCurrentProcessInstanceState() {
429: ProcessInstanceImpl processInstance = mCurrentProcessInstanceRef
430: .get();
431: if (processInstance != null) {
432: return processInstance.getState();
433: } else {
434: return ProcessInstance.STATE_UNKNOWN;
435: }
436: }
437:
438: public void setCurrentProcessInstance(
439: ProcessInstance processInstance) {
440: ProcessInstanceImpl newProcessInstance = (ProcessInstanceImpl) processInstance;
441:
442: // Automatically switch oto the first suspended process instance in
443: // case we're about to lose the current one
444: if (newProcessInstance == null) {
445: for (ProcessInstanceImpl instance : mProcessInstancesModel
446: .getProcessInstances()) {
447: if (instance.getState() == ProcessInstance.STATE_SUSPENDED) {
448: newProcessInstance = instance;
449: break;
450: }
451: }
452: }
453:
454: final ProcessInstanceImpl oldProcessInstance = mCurrentProcessInstanceRef
455: .getAndSet(newProcessInstance);
456:
457: firePropertyChange(PROP_CURRENT_PROCESS_INSTANCE,
458: oldProcessInstance, newProcessInstance);
459:
460: if (newProcessInstance != null) {
461: setCurrentPosition(newProcessInstance.getCurrentPosition());
462: } else {
463: setCurrentPosition(null);
464: }
465: }
466:
467: private void traceDebugException(String message, Exception exception) {
468: if (exception == null) {
469: getTracer().println(message); // the message is already localized
470: return;
471: }
472:
473: final String sep = NbBundle.getMessage(BpelDebuggerImpl.class,
474: "ERR_Separator");
475:
476: StringBuffer sb = new StringBuffer(200);
477: if (message != null) {
478: sb.append(message);
479: }
480: if (exception.getMessage() != null) {
481: if (sb.length() > 0) {
482: sb.append(sep);
483: }
484: sb.append(exception.getMessage());
485: }
486:
487: if (exception.getCause() != null) {
488: Throwable cause;
489: if (exception.getCause() instanceof UndeclaredThrowableException) {
490: cause = exception.getCause().getCause();
491: } else {
492: cause = exception.getCause();
493: }
494:
495: if (cause.getMessage() != null) {
496: if (sb.length() > 0) {
497: sb.append(sep);
498: }
499: sb.append(cause.getMessage());
500: }
501: }
502:
503: getTracer().println(sb.toString());
504: }
505:
506: public void clearRunToCursorBreakpoint() {
507: mRunToCursorBreakpoint = null;
508: }
509:
510: public SBYNActivityBreakpoint getRunToCursorBreakpoint() {
511: return mRunToCursorBreakpoint;
512: }
513:
514: public void runToCursor(LineBreakpoint breakpoint) {
515: mRunToCursorBreakpoint = new SBYNActivityBreakpoint(breakpoint,
516: this );
517: resume();
518: }
519:
520: ////////////////////////////////////////////////////////////////////////////
521: // Inner Classes
522: private class ProcessInstancesModelListener implements
523: ProcessInstancesModel.Listener {
524:
525: public void processInstanceRemoved(
526: final ProcessInstance processInstance) {
527:
528: if (processInstance == getCurrentProcessInstance()) {
529: setCurrentProcessInstance(null);
530: }
531: }
532:
533: public void processInstanceAdded(
534: final ProcessInstance processInstance) {
535:
536: // Does nothing
537: }
538:
539: public void processInstanceStateChanged(
540: final ProcessInstance processInstance,
541: final int oldState, final int newState) {
542:
543: if (newState == ProcessInstance.STATE_SUSPENDED) {
544: setCurrentProcessInstance(processInstance);
545: } else if (processInstance == getCurrentProcessInstance()) {
546: setCurrentPosition(null);
547: }
548: }
549: }
550: }
|