/** Must be called on process execution thread! Gets infos about the current threads. */ protected ProcessThreadInfo[] getThreadInfos() { List ret = null; ThreadContext tc = instance.getThreadContext(); Set threads = tc.getAllThreads(); if (threads != null) { ret = new ArrayList(); for (Iterator it = threads.iterator(); it.hasNext(); ) { ProcessThread pt = (ProcessThread) it.next(); ret.add( new ProcessThreadInfo( pt.getId(), pt.getActivity(), // pt.getLastEdge(), pt.getException(), pt.isWaiting(), pt.getData() == null ? new HashMap() : new HashMap(pt.getData()))); } } return (ProcessThreadInfo[]) ret.toArray(new ProcessThreadInfo[ret.size()]); }
/** Create an agenda panel. */ public ProcessViewPanel(final BpmnInterpreter instance, IBreakpointPanel bpp) { this.instance = instance; this.bpp = bpp; this.ptmodel = new ProcessThreadModel(); this.hmodel = new HistoryModel(); // todo: problem should be called on process execution thread! instance.setHistoryEnabled(true); // Todo: Disable history on close? threads_clone = getThreadInfos(); history_clone = instance.getHistory().toArray(); TableSorter sorter = new TableSorter(ptmodel); this.threads = new JTable(sorter); ResizeableTableHeader header = new ResizeableTableHeader(threads.getColumnModel()); header.setIncludeHeaderWidth(true); // threads.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); threads.setTableHeader(header); threads.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); sorter.setTableHeader(header); threads.getColumnModel().setColumnMargin(10); sorter = new TableSorter(hmodel); this.history = new JTable(sorter); header = new ResizeableTableHeader(history.getColumnModel()); header.setIncludeHeaderWidth(true); // history.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); history.setTableHeader(header); history.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); sorter.setTableHeader(header); history.getColumnModel().setColumnMargin(10); this.listener = new IChangeListener() { ProcessThreadInfo[] threads_clone; Object[] history_clone; Object next; boolean invoked; public void changeOccurred(ChangeEvent event) { synchronized (ProcessViewPanel.this) { List his = instance.getHistory(); threads_clone = getThreadInfos(); history_clone = his != null ? his.toArray() : new Object[0]; // next = instance.getNextActivation(); } if (!invoked) { invoked = true; SwingUtilities.invokeLater( new Runnable() { public void run() { invoked = false; synchronized (ProcessViewPanel.this) { ProcessViewPanel.this.threads_clone = threads_clone; ProcessViewPanel.this.history_clone = history_clone; ProcessViewPanel.this.next = next; } updateViews(); } }); } } }; instance.addChangeListener(listener); SwingUtilities.invokeLater( new Runnable() { public void run() { updateViews(); } }); JButton clear = new JButton("Clear"); clear.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { // todo: invoke on agent thread with invoke later // works because history is synchronized. List his = instance.getHistory(); if (his != null) { his.clear(); history_clone = new Object[0]; history.repaint(); } } }); final JCheckBox hon = new JCheckBox("Store History"); hon.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { // todo: invoke on agent thread with invoke later instance.setHistoryEnabled(hon.isSelected()); } }); hon.setSelected(true); JPanel procp = new JPanel(new BorderLayout()); procp.add(new JScrollPane(threads)); procp.setBorder(BorderFactory.createTitledBorder("Processes")); JPanel historyp = new JPanel(new BorderLayout()); historyp.add(new JScrollPane(history)); historyp.setBorder(BorderFactory.createTitledBorder("History")); JPanel buts = new JPanel(new FlowLayout(FlowLayout.RIGHT)); buts.add(hon); buts.add(clear); historyp.add(buts, BorderLayout.SOUTH); JSplitPane tmp = new JSplitPane(JSplitPane.VERTICAL_SPLIT); tmp.add(procp); tmp.add(historyp); tmp.setDividerLocation(200); // Hack?! setLayout(new BorderLayout()); add(tmp, BorderLayout.CENTER); }
/** * Make a process step, i.e. find the next edge or activity for a just executed thread. * * @param activity The activity to execute. * @param instance The process instance. * @param thread The process thread. */ public void step( MActivity activity, BpmnInterpreter instance, ProcessThread thread, Object event) { // System.out.println(instance.getComponentIdentifier().getLocalName()+": step "+activity+", // data "+thread.getData()); // Hack!!! Should be in interpreter/thread? thread.updateParametersAfterStep(activity, instance); MNamedIdElement next = null; ThreadContext remove = null; // Context that needs to be removed (if any). Exception ex = thread.getException(); // Store event (if any). if (event != null) { thread.setParameterValue("$event", event); // System.out.println("Event: "+activity+" "+thread+" "+event); } // Timer occurred flow if (AbstractEventIntermediateTimerActivityHandler.TIMER_EVENT.equals(event)) { // Cancel subflows. remove = thread.getThreadContext().getSubcontext(thread); // Continue with timer edge. List outedges = activity.getOutgoingSequenceEdges(); if (outedges != null && outedges.size() == 1) { next = (MSequenceEdge) outedges.get(0); } else if (outedges != null && outedges.size() > 1) { throw new RuntimeException("Cannot determine outgoing edge: " + activity); } } // Find next element and context(s) to be removed. else { boolean outside = false; ThreadContext context = thread.getThreadContext(); while (next == null && !outside) { // Normal flow if (ex == null) { List outgoing = activity.getOutgoingSequenceEdges(); if (outgoing != null && outgoing.size() == 1) { next = (MSequenceEdge) outgoing.get(0); } else if (outgoing != null && outgoing.size() > 1) { throw new UnsupportedOperationException( "Activity has more than one one outgoing edge. Please overridge step() for disambiguation: " + activity); } // else no outgoing edge -> check parent context, if any. } // Exception flow. else { List handlers = activity.getEventHandlers(); for (int i = 0; handlers != null && next == null && i < handlers.size(); i++) { MActivity handler = (MActivity) handlers.get(i); if (handler.getActivityType().equals("EventIntermediateError")) { // Todo: match exception types. // Class clazz = handler.getName()!=null ? SReflect.findClass0(clname, imports, // classloader); next = handler; } } } outside = context.getParent() == null; if (next == null && !outside) { // When last thread or exception, mark current context for removal. if (context.getThreads().size() == 1 || ex != null) { activity = (MActivity) context.getModelElement(); remove = context; context = context.getParent(); // Cancel subprocess handlers. if (activity instanceof MSubProcess) { List handlers = activity.getEventHandlers(); for (int i = 0; handlers != null && i < handlers.size(); i++) { MActivity handler = (MActivity) handlers.get(i); instance .getActivityHandler(handler) .cancel(handler, instance, remove.getInitiator()); } } } // If more threads are available in current context just exit loop. else if (context.getThreads().size() > 1) { outside = true; } } } } // Remove inner context(s), if any. if (remove != null) { thread = remove.getInitiator(); thread.setNonWaiting(); // Todo: Callbacks for aborted threads (to abort external activities) thread.getThreadContext().removeSubcontext(remove); } if (next != null) { // Todo: store exception as parameter!? if (ex != null) thread.setException(null); } else if (ex != null) { if (ex instanceof RuntimeException) throw (RuntimeException) ex; else throw new RuntimeException("Unhandled exception in process: " + activity, ex); } // Perform step settings, i.e. set next edge/activity or remove thread. if (next instanceof MSequenceEdge) { thread.setLastEdge((MSequenceEdge) next); } else if (next instanceof MActivity) { thread.setActivity((MActivity) next); } else if (next == null) { thread.getThreadContext().removeThread(thread); } else { throw new UnsupportedOperationException("Unknown outgoing element type: " + next); } }