/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.ode.bpel.engine; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.bdi.breaks.Breakpoint; import org.apache.ode.bpel.common.ProcessState; import org.apache.ode.bpel.dao.BpelDAOConnection; import org.apache.ode.bpel.dao.ProcessDAO; import org.apache.ode.bpel.dao.ProcessInstanceDAO; import org.apache.ode.bpel.evt.ActivityExecStartEvent; import org.apache.ode.bpel.evt.BpelEvent; import org.apache.ode.bpel.evt.ProcessCompletionEvent; import org.apache.ode.bpel.evt.ProcessInstanceEvent; import org.apache.ode.bpel.evt.ProcessInstanceStateChangeEvent; import org.apache.ode.bpel.evt.ProcessTerminationEvent; import org.apache.ode.bpel.evt.ScopeCompletionEvent; import org.apache.ode.bpel.pmapi.BpelManagementFacade; import org.apache.ode.bpel.pmapi.InstanceNotFoundException; import org.apache.ode.bpel.pmapi.ManagementException; import org.apache.ode.bpel.pmapi.ProcessingException; import org.apache.ode.bpel.runtime.breaks.BreakpointImpl; import org.apache.ode.utils.CollectionUtils; import org.apache.ode.utils.msg.MessageBundle; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Class providing functions used to support debugging funtionality * in the BPEL engine. This class serves as the underlying * implementation of the {@link BpelManagementFacade} interface, and * the various MBean interfaces. * * @todo Need to revisit the whole stepping/suspend/resume mechanism. */ class DebuggerSupport { private static final Log __log = LogFactory.getLog(DebuggerSupport.class); private static final Messages __msgs = MessageBundle.getMessages(Messages.class); static final Breakpoint[] EMPTY_BP = new Breakpoint[0]; private boolean _enabled = true; private Breakpoint[] _globalBreakPoints = EMPTY_BP; private final Set<Long> _step = new HashSet<Long>(); private final Map<Long, Breakpoint[]>_instanceBreakPoints = new HashMap<Long, Breakpoint[]>(); /** BPEL process database */ private BpelProcessDatabase _db; /** BPEL process. */ private BpelProcess _process; /** * Constructor. * @param db BPEL process database */ DebuggerSupport(BpelProcess process) { _process = process; _db = new BpelProcessDatabase(_process._engine._contexts.dao, _process._engine._contexts.scheduler, _process._pid); } void enable(boolean enabled){ _enabled = enabled; } Breakpoint[] getGlobalBreakpoints(){ return _globalBreakPoints; } Breakpoint[] getBreakpoints(Long pid){ Breakpoint[] arr = _instanceBreakPoints.get(pid); return (arr == null) ? EMPTY_BP : arr; } void addGlobalBreakpoint(Breakpoint breakpoint){ Collection<Breakpoint> c = CollectionUtils.makeCollection(ArrayList.class, _globalBreakPoints); c.add(breakpoint); _globalBreakPoints = c.toArray(new Breakpoint[c.size()]); } void addBreakpoint(Long pid, Breakpoint breakpoint){ Breakpoint[] bpArr = _instanceBreakPoints.get(pid); if(bpArr == null) { bpArr = new Breakpoint[]{breakpoint}; } else{ Collection<Breakpoint> c = CollectionUtils.makeCollection(ArrayList.class, bpArr); c.add(breakpoint); bpArr = c.toArray(new Breakpoint[c.size()]); } _instanceBreakPoints.put(pid, bpArr); } void removeGlobalBreakpoint(Breakpoint breakpoint){ Collection<Breakpoint> c = CollectionUtils.makeCollection(ArrayList.class, _globalBreakPoints); c.remove(breakpoint); _globalBreakPoints = c.toArray(new Breakpoint[c.size()]); } void removeBreakpoint(Long pid, Breakpoint breakpoint){ Breakpoint[] bpArr = _instanceBreakPoints.get(pid); if(bpArr != null){ Collection<Breakpoint> c = CollectionUtils.makeCollection(ArrayList.class, bpArr); c.remove(breakpoint); bpArr = c.toArray(new Breakpoint[c.size()]); if(bpArr.length == 0) { _instanceBreakPoints.remove(pid); } else { _instanceBreakPoints.put(pid, bpArr); } } } public boolean step(final Long iid) { boolean doit = false; try { doit = _db.exec(new BpelDatabase.Callable<Boolean>() { public Boolean run(BpelDAOConnection conn) throws Exception { ProcessInstanceDAO instance = conn.getInstance(iid); if (instance == null) throw new InstanceNotFoundException("" + iid); if(ProcessState.STATE_SUSPENDED == instance.getState()){ // send event ProcessInstanceStateChangeEvent evt = new ProcessInstanceStateChangeEvent(); evt.setOldState(ProcessState.STATE_SUSPENDED); short previousState = instance.getPreviousState(); instance.setState(previousState); evt.setNewState(previousState); evt.setProcessInstanceId(iid); evt.setProcessName(instance.getProcess().getType()); evt.setProcessId(_db.getProcessId()); _process.saveEvent(evt, instance); onEvent(evt); __log.debug("step(" + iid + ") adding step indicator to table."); _step.add(iid); WorkEvent we = new WorkEvent(); we.setIID(iid); we.setType(WorkEvent.Type.RESUME); _process._engine._contexts.scheduler.schedulePersistedJob(we.getDetail(), null); return true; } return false; } }); } catch (InstanceNotFoundException infe) { throw infe; } catch (Exception ex) { __log.error("UnexpectedEx", ex); throw new RuntimeException(ex); } return doit; } /** * Process BPEL events WRT debugging. * @param event BPEL event */ public void onEvent(BpelEvent event) { if(_enabled && (event instanceof ProcessInstanceEvent) && // I have this excluded since we are recursing here when onEvent() // is called from DebugSupport codepath's which change state !(event instanceof ProcessInstanceStateChangeEvent)) { final ProcessInstanceEvent evt = (ProcessInstanceEvent)event; // // prevent leaking of memory // if(evt instanceof ProcessCompletionEvent || evt instanceof ProcessTerminationEvent) { _step.remove(evt.getProcessInstanceId()); _instanceBreakPoints.remove(evt.getProcessInstanceId()); return; } boolean suspend = checkStep(evt); if (!suspend) { suspend = checkBreakPoints(evt, _globalBreakPoints); } if (!suspend){ Breakpoint[] bp = _instanceBreakPoints.get(evt.getProcessInstanceId()); if(bp != null) { suspend = checkBreakPoints(evt, bp); } } if(suspend){ _step.remove(evt.getProcessInstanceId()); try { ProcessDAO process = _db.getProcessDAO(); ProcessInstanceDAO instance = process.getInstance(evt.getProcessInstanceId()); if(ProcessState.canExecute(instance.getState())){ // send event ProcessInstanceStateChangeEvent changeEvent = new ProcessInstanceStateChangeEvent(); changeEvent.setOldState(instance.getState()); instance.setState(ProcessState.STATE_SUSPENDED); changeEvent.setNewState(ProcessState.STATE_SUSPENDED); changeEvent.setProcessInstanceId(instance.getInstanceId()); changeEvent.setProcessName(process.getType()); changeEvent.setProcessId(_db.getProcessId()); _process.saveEvent(changeEvent, instance); onEvent(changeEvent); } } catch (Exception dce) { __log.error(__msgs.msgDbError(), dce); } } } } private boolean checkStep(ProcessInstanceEvent event){ Long pid = event.getProcessInstanceId(); return (_step.contains(pid) && (event instanceof ActivityExecStartEvent || event instanceof ScopeCompletionEvent)); } private boolean checkBreakPoints(ProcessInstanceEvent event, Breakpoint[] breakpoints){ boolean suspended = false; for(int i = 0; i < breakpoints.length; ++i){ if (((BreakpointImpl)breakpoints[i]).checkBreak(event)){ suspended = true; break; } } return suspended; } public boolean resume(final Long iid) { boolean doit = false; try { doit = _db.exec(new BpelDatabase.Callable<Boolean>() { public Boolean run(BpelDAOConnection conn) throws Exception { ProcessInstanceDAO instance = conn.getInstance(iid); if (instance == null) throw new InstanceNotFoundException("" + iid); if(ProcessState.STATE_SUSPENDED == instance.getState()){ // send event ProcessInstanceStateChangeEvent evt = new ProcessInstanceStateChangeEvent(); evt.setOldState(ProcessState.STATE_SUSPENDED); short previousState = instance.getPreviousState(); instance.setState(previousState); evt.setNewState(previousState); evt.setProcessInstanceId(iid); evt.setProcessName(instance.getProcess().getType()); evt.setProcessId(_db.getProcessId()); _process.saveEvent(evt, instance); onEvent(evt); WorkEvent we = new WorkEvent(); we.setType(WorkEvent.Type.RESUME); we.setIID(iid); _process._engine._contexts.scheduler.schedulePersistedJob(we.getDetail(), null); return true; } return false; } }); } catch (InstanceNotFoundException infe) { throw infe; } catch (Exception ex) { __log.error("ProcessingEx", ex); throw new ProcessingException(ex.getMessage(),ex); } return doit; } public void suspend(final Long iid) { try { _db.exec(new BpelDatabase.Callable<Object>() { public Object run(BpelDAOConnection conn) throws Exception { ProcessInstanceDAO instance = conn.getInstance(iid); if (instance == null) { throw new InstanceNotFoundException("" + iid); } if (ProcessState.canExecute(instance.getState())) { // send event ProcessInstanceStateChangeEvent evt = new ProcessInstanceStateChangeEvent(); evt.setOldState(instance.getState()); instance.setState(ProcessState.STATE_SUSPENDED); evt.setNewState(ProcessState.STATE_SUSPENDED); evt.setProcessInstanceId(iid); ProcessDAO process = instance.getProcess(); evt.setProcessName(process.getType()); evt.setProcessId(process.getProcessId()); _process.saveEvent(evt, instance); onEvent(evt); } return null; } }); } catch (ManagementException me) { throw me; } catch (Exception ex) { __log.error("DbError", ex); throw new RuntimeException(ex); } } public void terminate(final Long iid) { try { _db.exec(new BpelDatabase.Callable<Object>() { public Object run(BpelDAOConnection conn) throws Exception { ProcessInstanceDAO instance = conn.getInstance(iid); if (instance == null) throw new ManagementException("InstanceNotFound:" + iid); // send event ProcessInstanceStateChangeEvent evt = new ProcessInstanceStateChangeEvent(); evt.setOldState(instance.getState()); instance.setState(ProcessState.STATE_TERMINATED); evt.setNewState(ProcessState.STATE_TERMINATED); evt.setProcessInstanceId(iid); ProcessDAO process = instance.getProcess(); QName processName = process.getType(); evt.setProcessName(processName); QName processId = process.getProcessId(); evt.setProcessId(processId); _process.saveEvent(evt, instance); // // TerminationEvent (peer of ProcessCompletionEvent) // ProcessTerminationEvent terminationEvent = new ProcessTerminationEvent(); terminationEvent.setProcessInstanceId(iid); terminationEvent.setProcessName(processName); terminationEvent.setProcessId(processId); _process.saveEvent(evt, instance); onEvent(evt); onEvent(terminationEvent); return null; } }); } catch (ManagementException me) { throw me; } catch (Exception e) { __log.error("DbError", e); throw new RuntimeException(e); } } }