/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.bpm.console.client.process; import java.util.ArrayList; import java.util.List; import com.google.gwt.core.client.GWT; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.user.client.ui.*; import org.gwt.mosaic.ui.client.ListBox; import org.gwt.mosaic.ui.client.MessageBox; import org.gwt.mosaic.ui.client.ToolBar; import org.gwt.mosaic.ui.client.event.RowSelectionEvent; import org.gwt.mosaic.ui.client.event.RowSelectionHandler; import org.gwt.mosaic.ui.client.layout.BorderLayout; import org.gwt.mosaic.ui.client.layout.BorderLayoutData; import org.gwt.mosaic.ui.client.layout.BoxLayout; import org.gwt.mosaic.ui.client.layout.BoxLayoutData; import org.gwt.mosaic.ui.client.layout.MosaicPanel; import org.gwt.mosaic.ui.client.list.DefaultListModel; import org.jboss.bpm.console.client.ApplicationContext; import org.jboss.bpm.console.client.common.DataDriven; import org.jboss.bpm.console.client.common.IFrameWindowCallback; import org.jboss.bpm.console.client.common.IFrameWindowPanel; import org.jboss.bpm.console.client.common.LoadingOverlay; import org.jboss.bpm.console.client.common.WidgetWindowPanel; import org.jboss.bpm.console.client.icons.ConsoleIconBundle; import org.jboss.bpm.console.client.model.ProcessDefinitionRef; import org.jboss.bpm.console.client.model.ProcessInstanceRef; import org.jboss.bpm.console.client.model.TokenReference; import org.jboss.bpm.console.client.process.events.InstanceEvent; import org.jboss.bpm.console.client.process.events.SignalInstanceEvent; import org.jboss.bpm.console.client.util.SimpleDateFormat; import org.jboss.errai.workspaces.client.api.ProvisioningCallback; import org.jboss.errai.workspaces.client.api.WidgetProvider; import org.jboss.errai.workspaces.client.framework.Registry; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.mvc4g.client.Controller; import com.mvc4g.client.Event; import com.mvc4g.client.ViewInterface; /** * @author Heiko.Braun <heiko.braun@jboss.com> */ public class InstanceListView implements WidgetProvider, ViewInterface, DataDriven { public final static String ID = InstanceListView.class.getName(); private Controller controller; private MosaicPanel instanceList = null; private ListBox<ProcessInstanceRef> listBox; private ProcessDefinitionRef currentDefinition; private boolean isInitialized; private List<ProcessInstanceRef> cachedInstances = null; private SimpleDateFormat dateFormat = new SimpleDateFormat(); private ApplicationContext appContext; private IFrameWindowPanel iframeWindow = null; private boolean isRiftsawInstance; //private PagingPanel pagingPanel; MosaicPanel panel; private Button startBtn, terminateBtn, deleteBtn, signalBtn, refreshBtn; // elements needed to signal waiting execution private List<TokenReference> tokensToSignal = null; private WidgetWindowPanel signalWindowPanel; private ListBox<TokenReference> listBoxTokens = null; private List<TextBox> signalTextBoxes = null; private ListBox<String> listBoxTokenSignals; private ImageResource greenIcon; public void provideWidget(ProvisioningCallback callback) { this.appContext = Registry.get(ApplicationContext.class); // riftsaw? isRiftsawInstance = appContext.getConfig().getProfileName().equals("BPEL Console"); panel = new MosaicPanel(); panel.setPadding(0); Registry.get(Controller.class).addView(ID, this); initialize(); ConsoleIconBundle imageBundle = (ConsoleIconBundle)GWT.create(ConsoleIconBundle.class); greenIcon = imageBundle.greenIcon(); callback.onSuccess(panel); } public boolean isInitialized() { return isInitialized; } public void initialize() { if(!isInitialized) { instanceList = new MosaicPanel( new BoxLayout(BoxLayout.Orientation.VERTICAL)); instanceList.setPadding(0); instanceList.setWidgetSpacing(0); listBox = new ListBox<ProcessInstanceRef>( new String[] { "<b>Instance</b>", "State", "Start Date"} ); listBox.setCellRenderer(new ListBox.CellRenderer<ProcessInstanceRef>() { public void renderCell(ListBox<ProcessInstanceRef> listBox, int row, int column, ProcessInstanceRef item) { switch (column) { case 0: listBox.setText(row, column, item.getId()); break; case 1: listBox.setText(row, column, item.getState().toString()); break; case 2: String d = item.getStartDate() != null ? dateFormat.format(item.getStartDate()) : ""; listBox.setText(row, column, d); break; default: throw new RuntimeException("Unexpected column size"); } } }); listBox.addRowSelectionHandler( new RowSelectionHandler() { public void onRowSelection(RowSelectionEvent rowSelectionEvent) { int index = listBox.getSelectedIndex(); if(index!=-1) { ProcessInstanceRef item = listBox.getItem(index); // enable or disable signal button depending on current activity if (isSignalable(item)) { signalBtn.setEnabled(true); } else { signalBtn.setEnabled(false); } terminateBtn.setEnabled(true); // update details controller.handleEvent( new Event(UpdateInstanceDetailAction.ID, new InstanceEvent(currentDefinition, item) ) ); } } } ); // toolbar final MosaicPanel toolBox = new MosaicPanel(); toolBox.setPadding(0); toolBox.setWidgetSpacing(5); final ToolBar toolBar = new ToolBar(); refreshBtn = new Button("Refresh", new ClickHandler() { public void onClick(ClickEvent clickEvent) { controller.handleEvent( new Event( UpdateInstancesAction.ID, getCurrentDefinition() ) ); } } ); toolBar.add(refreshBtn); refreshBtn.setEnabled(false); toolBar.addSeparator(); startBtn = new Button("Start", new ClickHandler() { public void onClick(ClickEvent clickEvent) { MessageBox.confirm("Start new execution", "Do you want to start a new execution of this process?", new MessageBox.ConfirmationCallback() { public void onResult(boolean doIt) { if (doIt) { String url = getCurrentDefinition().getFormUrl(); boolean hasForm = (url != null && !url.equals("")); if (hasForm) { ProcessDefinitionRef definition = getCurrentDefinition(); iframeWindow = new IFrameWindowPanel( definition.getFormUrl(), "New Process Instance: " + definition.getId() ); iframeWindow.setCallback( new IFrameWindowCallback() { public void onWindowClosed() { controller.handleEvent( new Event(UpdateInstancesAction.ID, getCurrentDefinition()) ); } } ); iframeWindow.show(); } else { controller.handleEvent( new Event( StartNewInstanceAction.ID, getCurrentDefinition() ) ); } } } }); } } ); terminateBtn = new Button("Terminate", new ClickHandler() { public void onClick(ClickEvent clickEvent) { if (getSelection() != null) { MessageBox.confirm("Terminate instance", "Terminating this instance will stop further execution.", new MessageBox.ConfirmationCallback() { public void onResult(boolean doIt) { if (doIt) { ProcessInstanceRef selection = getSelection(); selection.setState(ProcessInstanceRef.STATE.ENDED); selection.setEndResult(ProcessInstanceRef.RESULT.OBSOLETE); controller.handleEvent( new Event( StateChangeAction.ID, selection ) ); } } }); } else { MessageBox.alert("Missing selection", "Please select an instance"); } } } ); deleteBtn = new Button("Delete", new ClickHandler() { public void onClick(ClickEvent clickEvent) { if (getSelection() != null) { MessageBox.confirm("Delete instance", "Deleting this instance will remove any history information and associated tasks as well.", new MessageBox.ConfirmationCallback() { public void onResult(boolean doIt) { if (doIt) { ProcessInstanceRef selection = getSelection(); selection.setState(ProcessInstanceRef.STATE.ENDED); controller.handleEvent( new Event( DeleteInstanceAction.ID, selection ) ); } } }); } else { MessageBox.alert("Missing selection", "Please select an instance"); } } } ); signalBtn = new Button("Signal", new ClickHandler() { public void onClick(ClickEvent clickEvent) { createSignalWindow(); } } ); if(!isRiftsawInstance) // riftsaw doesn't support instance operations { toolBar.add(startBtn); toolBar.add(signalBtn); toolBar.add(deleteBtn); startBtn.setEnabled(false); deleteBtn.setEnabled(false); signalBtn.setEnabled(false); } // terminate works on any BPM Engine toolBar.add(terminateBtn); terminateBtn.setEnabled(false); toolBox.add(toolBar, new BoxLayoutData(BoxLayoutData.FillStyle.HORIZONTAL)); instanceList.add(toolBox, new BoxLayoutData(BoxLayoutData.FillStyle.HORIZONTAL)); instanceList.add(listBox, new BoxLayoutData(BoxLayoutData.FillStyle.BOTH)); /*pagingPanel = new PagingPanel( new PagingCallback() { public void rev() { renderUpdate(); } public void ffw() { renderUpdate(); } } ); instanceList.add(pagingPanel, new BoxLayoutData(BoxLayoutData.FillStyle.HORIZONTAL));*/ // cached data? if(this.cachedInstances!=null) bindData(this.cachedInstances); // layout MosaicPanel layout = new MosaicPanel(new BorderLayout()); layout.setPadding(0); layout.add(instanceList, new BorderLayoutData(BorderLayout.Region.CENTER)); // details InstanceDetailView detailsView = new InstanceDetailView(); controller.addView(InstanceDetailView.ID, detailsView); controller.addAction(UpdateInstanceDetailAction.ID, new UpdateInstanceDetailAction()); controller.addAction(ClearInstancesAction.ID, new ClearInstancesAction()); controller.addAction(SignalExecutionAction.ID, new SignalExecutionAction()); layout.add(detailsView, new BorderLayoutData(BorderLayout.Region.SOUTH,10,200)); panel.add(layout); isInitialized = true; } } public ProcessInstanceRef getSelection() { ProcessInstanceRef selection = null; if(listBox.getSelectedIndex()!=-1) selection = listBox.getItem( listBox.getSelectedIndex()); return selection; } public ProcessDefinitionRef getCurrentDefinition() { return this.currentDefinition; } public void setController(Controller controller) { this.controller = controller; } public void reset() { this.currentDefinition = null; this.cachedInstances = new ArrayList<ProcessInstanceRef>(); renderUpdate(); startBtn.setEnabled(false); terminateBtn.setEnabled(false); deleteBtn.setEnabled(false); signalBtn.setEnabled(false); refreshBtn.setEnabled(false); } public void update(Object... data) { this.currentDefinition = (ProcessDefinitionRef)data[0]; this.cachedInstances = (List<ProcessInstanceRef>)data[1]; //if(isInitialized()) pagingPanel.reset(); renderUpdate(); } public void setLoading(boolean isLoading) { LoadingOverlay.on(instanceList, isLoading); } private void renderUpdate() { if(isInitialized()) { bindData(this.cachedInstances); // clear details controller.handleEvent( new Event(UpdateInstanceDetailAction.ID, new InstanceEvent(this.currentDefinition, null) ) ); startBtn.setEnabled(true); deleteBtn.setEnabled(true); refreshBtn.setEnabled(true); } } private void bindData(List<ProcessInstanceRef> instances) { final DefaultListModel<ProcessInstanceRef> model = (DefaultListModel<ProcessInstanceRef>) listBox.getModel(); model.clear(); List<ProcessInstanceRef> list = instances;//pagingPanel.trim(instances); for(ProcessInstanceRef inst : list) { model.add(inst); } // layout again panel.invalidate(); } private boolean isSignalable(ProcessInstanceRef processInstance) { tokensToSignal = new ArrayList<TokenReference>(); // first check if the parent execution is signalable if (processInstance.getRootToken() != null && processInstance.getRootToken().canBeSignaled()) { tokensToSignal.add(processInstance.getRootToken()); } else if (processInstance.getRootToken() != null && processInstance.getRootToken().getChildren() != null) { // next verify children collectSignalableTokens(processInstance.getRootToken(), tokensToSignal); } if (tokensToSignal.size() > 0) { return true; } else { return false; } } private void collectSignalableTokens(TokenReference tokenParent, List<TokenReference> tokensToSignal) { if (tokenParent.getChildren() != null) { for (TokenReference token : tokenParent.getChildren()) { if (token.canBeSignaled()) { tokensToSignal.add(token); } collectSignalableTokens(token, tokensToSignal); } } } private void createSignalWindow() { signalTextBoxes = new ArrayList<TextBox>(); MosaicPanel layout = new MosaicPanel(new BoxLayout(BoxLayout.Orientation.VERTICAL)); layout.setStyleName("bpm-window-layout"); layout.setPadding(5); // toolbar final MosaicPanel toolBox = new MosaicPanel(); toolBox.setPadding(0); toolBox.setWidgetSpacing(5); toolBox.setLayout(new BoxLayout(BoxLayout.Orientation.HORIZONTAL)); final ToolBar toolBar = new ToolBar(); toolBar.add( new Button("Signal", new ClickHandler() { public void onClick(ClickEvent clickEvent) { int selectedToken = listBoxTokens.getSelectedIndex(); int selectedSignal = listBoxTokenSignals.getSelectedIndex(); if (selectedToken != -1 && selectedSignal != -1) { controller.handleEvent( new Event(SignalExecutionAction.ID, new SignalInstanceEvent(getCurrentDefinition(), getSelection(), listBoxTokens.getItem(selectedToken), listBoxTokenSignals.getItem(selectedSignal), selectedToken))); } else { MessageBox.alert("Incomplete selection", "Please select both token and signal name"); } } } ) ); toolBar.add( new Button("Cancel", new ClickHandler() { public void onClick(ClickEvent clickEvent) { signalWindowPanel.close(); } } ) ); Label header = new Label("Available tokens to signal: "); header.setStyleName("bpm-label-header"); layout.add(header, new BoxLayoutData(BoxLayoutData.FillStyle.HORIZONTAL)); toolBox.add(toolBar, new BoxLayoutData(BoxLayoutData.FillStyle.HORIZONTAL)); layout.add(toolBox, new BoxLayoutData(BoxLayoutData.FillStyle.HORIZONTAL)); listBoxTokens = new ListBox<TokenReference>(new String[] { "Id", "Name" }); listBoxTokens.setCellRenderer(new ListBox.CellRenderer<TokenReference>() { public void renderCell(ListBox<TokenReference> listBox, int row, int column, TokenReference item) { switch (column) { case 0: listBox.setText(row, column, item.getId()); break; case 1: listBox.setText(row, column, item.getName() == null ? item.getCurrentNodeName() : item.getName()); break; default: throw new RuntimeException("Unexpected column size"); } } }); listBoxTokens.addRowSelectionHandler( new RowSelectionHandler() { public void onRowSelection(RowSelectionEvent rowSelectionEvent) { int index = listBoxTokens.getSelectedIndex(); if(index!=-1) { TokenReference item = listBoxTokens.getItem(index); renderAvailableSignals(item); } } } ); renderSignalListBox(-1); layout.add(listBoxTokens, new BoxLayoutData(BoxLayoutData.FillStyle.BOTH)); Label headerSignals = new Label("Available signal names"); headerSignals.setStyleName("bpm-label-header"); layout.add(headerSignals, new BoxLayoutData(BoxLayoutData.FillStyle.HORIZONTAL)); listBoxTokenSignals = new ListBox<String>(new String[] { "Signal name" }); listBoxTokenSignals.setCellRenderer(new ListBox.CellRenderer<String>() { public void renderCell(ListBox<String> listBox, int row, int column, String item) { switch (column) { case 0: listBox.setText(row, column, item); break; default: throw new RuntimeException("Unexpected column size"); } } }); layout.add(listBoxTokenSignals, new BoxLayoutData(BoxLayoutData.FillStyle.BOTH)); signalWindowPanel = new WidgetWindowPanel( "Signal proces from wait state", layout, true ); } public void renderSignalListBox(int i) { // remove currently signaled token if (i > -1) { tokensToSignal.remove(i); } // if available token list is empty close window if (tokensToSignal.isEmpty()) { signalWindowPanel.close(); } // display all remaining token possible to signal ((DefaultListModel<TokenReference>)listBoxTokens.getModel()).clear(); for (TokenReference token : tokensToSignal) { ((DefaultListModel<TokenReference>)listBoxTokens.getModel()).add(token); } // clear available signal list box if (listBoxTokenSignals != null) { ((DefaultListModel<String>)listBoxTokenSignals.getModel()).clear(); } } private void renderAvailableSignals(TokenReference item) { ((DefaultListModel<String>)listBoxTokenSignals.getModel()).clear(); for (String signal : item.getAvailableSignals()) { ((DefaultListModel<String>)listBoxTokenSignals.getModel()).add(signal); } } }