/* Licensed 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.activiti.explorer.ui.process.simple.editor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.util.List; import java.util.UUID; import org.activiti.engine.ProcessEngineConfiguration; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RepositoryService; import org.activiti.engine.impl.ProcessEngineImpl; import org.activiti.engine.repository.Model; import org.activiti.explorer.ExplorerApp; import org.activiti.explorer.Messages; import org.activiti.explorer.ui.AbstractPage; import org.activiti.explorer.ui.custom.DetailPanel; import org.activiti.explorer.ui.custom.ToolBar; import org.activiti.explorer.ui.custom.ToolbarEntry.ToolbarCommand; import org.activiti.explorer.ui.mainlayout.ExplorerLayout; import org.activiti.explorer.ui.process.simple.editor.table.TaskTable; import org.activiti.image.ProcessDiagramGenerator; import org.activiti.workflow.simple.converter.WorkflowDefinitionConversion; import org.activiti.workflow.simple.definition.HumanStepDefinition; import org.activiti.workflow.simple.definition.ListStepDefinition; import org.activiti.workflow.simple.definition.ParallelStepsDefinition; import org.activiti.workflow.simple.definition.StepDefinition; import org.activiti.workflow.simple.definition.StepDefinitionContainer; import org.activiti.workflow.simple.definition.WorkflowDefinition; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.terminal.StreamResource; import com.vaadin.terminal.StreamResource.StreamSource; import com.vaadin.ui.AbstractSelect; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Embedded; import com.vaadin.ui.GridLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Panel; import com.vaadin.ui.TextField; import com.vaadin.ui.themes.Reindeer; /** * @author Joram Barrez */ public class SimpleTableEditor extends AbstractPage { private static final long serialVersionUID = -4430424035744622992L; private static final Logger logger = LoggerFactory.getLogger(SimpleTableEditor.class); private static final String KEY_EDITOR = "editor"; private static final String KEY_PREVIEW = "preview"; // Input when creating new process protected String workflowName; protected String description; // Input when updating existing proceess protected String modelId; protected WorkflowDefinition workflowDefinition; // ui protected DetailPanel mainLayout; protected GridLayout editorGrid; protected TextField nameField; protected TextField descriptionField; protected TaskTable taskTable; protected Panel imagePanel; /** * Constructor used when creating a new process. */ public SimpleTableEditor(String workflowName, String description) { this.workflowName = workflowName; this.description = description; } /** * Constructor used when updating an existing process */ public SimpleTableEditor(String modelId, WorkflowDefinition workflowDefinition) { this(workflowDefinition.getName(), workflowDefinition.getDescription()); this.modelId = modelId; this.workflowDefinition = workflowDefinition; } @Override protected void initUi() { super.initUi(); setSizeFull(); grid.setColumnExpandRatio(0, 0f); // Hide the column on the left side mainLayout = new DetailPanel(); setDetailComponent(mainLayout); // Editor editorGrid = new GridLayout(2, 7); editorGrid.setSizeFull(); editorGrid.setMargin(true); editorGrid.setColumnExpandRatio(0, 1.0f); editorGrid.setColumnExpandRatio(1, 9.0f); editorGrid.setSpacing(true); mainLayout.addComponent(editorGrid); initNameField(editorGrid); initDescriptionField(editorGrid); initTaskTable(editorGrid); initButtons(editorGrid); toolBar.setActiveEntry(KEY_EDITOR); } protected ToolBar initToolbar() { toolBar = new ToolBar(); toolBar.addToolbarEntry(KEY_EDITOR, ExplorerApp.get().getI18nManager().getMessage(Messages.PROCESS_EDITOR_TITLE), new ToolbarCommand() { public void toolBarItemSelected() { if (imagePanel != null) { imagePanel.setVisible(false); editorGrid.setVisible(true); toolBar.setActiveEntry(KEY_EDITOR); } } }); toolBar.addToolbarEntry(KEY_PREVIEW, ExplorerApp.get().getI18nManager().getMessage(Messages.PROCESS_EDITOR_BPMN_PREVIEW), new ToolbarCommand() { public void toolBarItemSelected() { editorGrid.setVisible(false); showDiagram(); toolBar.setActiveEntry(KEY_PREVIEW); } }); return toolBar; } protected void initNameField(GridLayout layout) { nameField = new TextField(); nameField.setWriteThrough(true); nameField.setImmediate(true); layout.addComponent(new Label(ExplorerApp.get().getI18nManager().getMessage(Messages.PROCESS_EDITOR_NAME))); layout.addComponent(nameField); if (workflowName != null) { nameField.setValue(workflowName); workflowName = null; } } protected void initDescriptionField(GridLayout layout) { descriptionField = new TextField(); descriptionField.setRows(4); descriptionField.setColumns(35); descriptionField.addStyleName(ExplorerLayout.STYLE_TEXTAREA_NO_RESIZE); layout.addComponent(new Label(ExplorerApp.get().getI18nManager().getMessage(Messages.PROCESS_EDITOR_DESCRIPTION))); layout.addComponent(descriptionField); if (description != null) { descriptionField.setValue(description); description = null; } } protected void initTaskTable(GridLayout layout) { taskTable = new TaskTable(); // Add existing tasks in case we're editing if (workflowDefinition != null) { loadTaskRows(workflowDefinition, taskTable); } else { taskTable.addDefaultTaskRow(); } layout.addComponent(new Label(ExplorerApp.get().getI18nManager().getMessage(Messages.PROCESS_EDITOR_TASKS))); layout.addComponent(taskTable); } protected void loadTaskRows(StepDefinitionContainer<?> container, TaskTable taskTable) { for (StepDefinition stepDefinition : container.getSteps()) { if (stepDefinition instanceof HumanStepDefinition) { HumanStepDefinition humanStepDefinition = (HumanStepDefinition) stepDefinition; taskTable.addTaskRow(humanStepDefinition); } else if (stepDefinition instanceof StepDefinitionContainer<?>) { loadTaskRows((StepDefinitionContainer<?>) stepDefinition, taskTable); } } } protected void initButtons(GridLayout layout) { final Button saveButton = new Button(ExplorerApp.get().getI18nManager().getMessage(Messages.PROCESS_EDITOR_SAVE)); saveButton.setEnabled(nameField.getValue() != null && !"".equals((String) nameField.getValue())); toolBar.addButton(saveButton); saveButton.addListener(new ClickListener() { private static final long serialVersionUID = 1L; public void buttonClick(ClickEvent event) { save(); } }); // Dependending on namefield value, save button is enabled nameField.addListener(new ValueChangeListener() { private static final long serialVersionUID = 1L; public void valueChange(ValueChangeEvent event) { if (nameField.getValue() != null && !"".equals((String) nameField.getValue())) { saveButton.setEnabled(true); } else { saveButton.setEnabled(false); } } }); } public TextField getNameTextField() { return nameField; } protected void showDiagram() { StreamResource.StreamSource streamSource = new StreamSource() { private static final long serialVersionUID = 6993112534181068935L; public InputStream getStream() { WorkflowDefinitionConversion workflowDefinitionConversion = ExplorerApp.get().getWorkflowDefinitionConversionFactory().createWorkflowDefinitionConversion(createWorkflow()); final ProcessEngineImpl defaultProcessEngine = (ProcessEngineImpl) ProcessEngines.getDefaultProcessEngine(); final ProcessEngineConfiguration processEngineConfiguration = defaultProcessEngine.getProcessEngineConfiguration(); final ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator(); return diagramGenerator.generateDiagram(workflowDefinitionConversion.getBpmnModel(), "png", processEngineConfiguration.getActivityFontName(), processEngineConfiguration.getLabelFontName(), processEngineConfiguration.getAnnotationFontName(), processEngineConfiguration.getClassLoader()); } }; // resource must have unique id (or cache-crap can happen)! StreamResource imageresource = new StreamResource(streamSource,UUID.randomUUID() + ".png", ExplorerApp.get()); Embedded diagram = new Embedded("", imageresource); diagram.setType(Embedded.TYPE_IMAGE); diagram.setSizeUndefined(); imagePanel = new Panel(); // using panel for scrollbars imagePanel.setScrollable(true); imagePanel.addStyleName(Reindeer.PANEL_LIGHT); imagePanel.setWidth(100, UNITS_PERCENTAGE); imagePanel.setHeight("100%"); mainLayout.addComponent(imagePanel); HorizontalLayout panelLayout = new HorizontalLayout(); panelLayout.setSizeUndefined(); imagePanel.setContent(panelLayout); imagePanel.addComponent(diagram); } protected void save() { WorkflowDefinition workflowDefinition = createWorkflow(); final ProcessEngineImpl defaultProcessEngine = (ProcessEngineImpl) ProcessEngines.getDefaultProcessEngine(); RepositoryService repositoryService = defaultProcessEngine.getRepositoryService(); ProcessEngineConfiguration processEngineConfiguration = defaultProcessEngine.getProcessEngineConfiguration(); ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator(); Model model = null; if (modelId == null) { // new process model = repositoryService.newModel(); } else { // update existing process model = repositoryService.getModel(modelId); } model.setName(workflowDefinition.getName()); model.setCategory(SimpleTableEditorConstants.TABLE_EDITOR_CATEGORY); repositoryService.saveModel(model); // Store model entity WorkflowDefinitionConversion conversion = ExplorerApp.get().getWorkflowDefinitionConversionFactory().createWorkflowDefinitionConversion(workflowDefinition); conversion.convert(); try { // Write JSON to byte-array and set as editor-source ByteArrayOutputStream baos = new ByteArrayOutputStream(); ExplorerApp.get().getSimpleWorkflowJsonConverter().writeWorkflowDefinition(workflowDefinition, new OutputStreamWriter(baos)); repositoryService.addModelEditorSource(model.getId(), baos.toByteArray()); // Store process image // TODO: we should really allow the service to take an inputstream as input. Now we load it into memory ... repositoryService.addModelEditorSourceExtra(model.getId(), IOUtils.toByteArray( diagramGenerator.generateDiagram(conversion.getBpmnModel(), "png", processEngineConfiguration.getActivityFontName(), processEngineConfiguration.getLabelFontName(), processEngineConfiguration.getAnnotationFontName(), processEngineConfiguration.getClassLoader()))); } catch (IOException e) { logger.warn("Could not generate process image. Image is not stored and will not be shown.", e); } ExplorerApp.get().getViewManager().showEditorProcessDefinitionPage(model.getId()); } protected WorkflowDefinition createWorkflow() { WorkflowDefinition workflow = new WorkflowDefinition(); workflow.setName((String) nameField.getValue()); String description = (String) descriptionField.getValue(); if (description != null && description.length() > 0) { workflow.setDescription(description); } List<HumanStepDefinition> steps = taskTable.getSteps(); for (int i=0; i<steps.size(); i++) { HumanStepDefinition currentStep = steps.get(i); // Check if we have a parallel block int nextIndex = i+1; ParallelStepsDefinition parallelStepsDefinition = null; while (nextIndex < steps.size() && steps.get(nextIndex).isStartsWithPrevious()) { if (parallelStepsDefinition == null) { parallelStepsDefinition = new ParallelStepsDefinition(); ListStepDefinition<ParallelStepsDefinition> listStepDef = new ListStepDefinition<ParallelStepsDefinition>(); listStepDef.addStep(currentStep); parallelStepsDefinition.addStepList(listStepDef); } ListStepDefinition<ParallelStepsDefinition> listStepDef = new ListStepDefinition<ParallelStepsDefinition>(); listStepDef.addStep(steps.get(nextIndex)); parallelStepsDefinition.addStepList(listStepDef); nextIndex++; } if (parallelStepsDefinition != null) { workflow.addStep(parallelStepsDefinition); i = nextIndex - 1; } else { workflow.addStep(currentStep); } } return workflow; } // Following are required by superclass, but not implemented since we do the addMainLayout ourselves @Override protected ToolBar createMenuBar() { return initToolbar(); } @Override protected AbstractSelect createSelectComponent() { return null; } @Override public void refreshSelectNext() { } @Override public void selectElement(int index) { } }