Java tutorial
/** * Licensed to JumpMind Inc under one or more contributor * license agreements. See the NOTICE file distributed * with this work for additional information regarding * copyright ownership. JumpMind Inc licenses this file * to you under the GNU General Public License, version 3.0 (GPLv3) * (the "License"); you may not use this file except in compliance * with the License. * * You should have received a copy of the GNU General Public License, * version 3.0 (GPLv3) along with this library; if not, see * <http://www.gnu.org/licenses/>. * * 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.jumpmind.metl.ui.views.manage; import static org.apache.commons.lang.StringUtils.isNotBlank; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jumpmind.metl.core.model.AbstractObject; import org.jumpmind.metl.core.model.Execution; import org.jumpmind.metl.core.model.ExecutionStatus; import org.jumpmind.metl.core.model.ExecutionStep; import org.jumpmind.metl.core.model.ExecutionStepLog; import org.jumpmind.metl.core.model.Flow; import org.jumpmind.metl.core.model.FlowStep; import org.jumpmind.metl.core.model.FlowStepLink; import org.jumpmind.metl.core.model.Setting; import org.jumpmind.metl.core.model.UserSetting; import org.jumpmind.metl.core.persist.IExecutionService; import org.jumpmind.metl.core.runtime.LogLevel; import org.jumpmind.metl.core.runtime.component.AbstractComponentRuntime; import org.jumpmind.metl.ui.common.ApplicationContext; import org.jumpmind.metl.ui.common.ButtonBar; import org.jumpmind.metl.ui.common.IBackgroundRefreshable; import org.jumpmind.metl.ui.common.Icons; import org.jumpmind.metl.ui.common.TabbedPanel; import org.jumpmind.metl.ui.common.UiUtils; import org.jumpmind.metl.ui.diagram.Node; import org.jumpmind.metl.ui.diagram.NodeSelectedEvent; import org.jumpmind.metl.ui.diagram.RunDiagram; import org.jumpmind.metl.ui.views.IFlowRunnable; import org.jumpmind.metl.ui.views.UiConstants; import org.jumpmind.util.AppUtils; import org.jumpmind.vaadin.ui.common.ConfirmDialog; import org.jumpmind.vaadin.ui.common.IUiPanel; import org.jumpmind.vaadin.ui.common.ImmediateUpdateTextField; import org.jumpmind.vaadin.ui.common.ReadOnlyTextAreaDialog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vaadin.data.Property; import com.vaadin.data.sort.SortOrder; import com.vaadin.data.util.BeanContainer; import com.vaadin.data.util.BeanItem; import com.vaadin.data.util.BeanItemContainer; import com.vaadin.data.util.filter.SimpleStringFilter; import com.vaadin.event.ItemClickEvent; import com.vaadin.server.FontAwesome; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.ui.AbstractLayout; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.CheckBox; import com.vaadin.ui.ComboBox; import com.vaadin.ui.Grid; import com.vaadin.ui.Grid.HeaderCell; import com.vaadin.ui.Grid.HeaderRow; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Panel; import com.vaadin.ui.Table; import com.vaadin.ui.Table.ColumnGenerator; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.VerticalSplitPanel; import com.vaadin.ui.renderers.DateRenderer; import com.vaadin.ui.themes.ValoTheme; public class ExecutionRunPanel extends VerticalLayout implements IUiPanel, IBackgroundRefreshable { private static final long serialVersionUID = 1L; final protected Logger log = LoggerFactory.getLogger(getClass()); IExecutionService executionService; VerticalSplitPanel splitPanel; Table stepTable = new Table(); RunDiagram diagram; Panel flowPanel; AbstractLayout diagramLayout; Flow flow; List<AbstractObject> selected = new ArrayList<AbstractObject>(); Grid logTable; BeanContainer<String, ExecutionStep> stepContainer = new BeanContainer<String, ExecutionStep>( ExecutionStep.class); BeanItemContainer<ExecutionStepLog> logContainer = new BeanItemContainer<ExecutionStepLog>( ExecutionStepLog.class); Label flowLabel = new Label(); Label statusLabel = new Label("", ContentMode.HTML); Label startLabel = new Label(); Label endLabel = new Label(); Button removeButton; Button cancelButton; Button rerunButton; CheckBox showDiagramCheckbox; TextField limitField; String executionId; ApplicationContext context; TabbedPanel parentTabSheet; IFlowRunnable flowRunnable; boolean lastDataRefreshWasDone = false; List<SortOrder> lastSortOrder; public ExecutionRunPanel(String executionId, ApplicationContext context, TabbedPanel parentTabSheet, IFlowRunnable flowRunnable) { this.executionService = context.getExecutionService(); this.executionId = executionId; this.context = context; this.parentTabSheet = parentTabSheet; this.flowRunnable = flowRunnable; Execution execution = executionService.findExecution(executionId); this.flow = context.getConfigurationService().findFlow(execution.getFlowId()); HorizontalLayout topBar = new HorizontalLayout(); topBar.setMargin(new MarginInfo(true, true, false, true)); topBar.setWidth(100, Unit.PERCENTAGE); HorizontalLayout left = new HorizontalLayout(); topBar.addComponent(left); HorizontalLayout right = new HorizontalLayout(); right.setSpacing(true); topBar.addComponent(right); topBar.setComponentAlignment(right, Alignment.MIDDLE_RIGHT); Label limitLabel = new Label("Max Log Messages To Show :"); right.addComponent(limitLabel); right.setComponentAlignment(limitLabel, Alignment.MIDDLE_RIGHT); limitField = new ImmediateUpdateTextField(null) { private static final long serialVersionUID = 1L; @Override protected void save(String text) { Setting setting = context.getUser().findSetting(UserSetting.SETTING_MAX_LOG_MESSAGE_TO_SHOW); setting.setValue(Integer.toString(getMaxToShow(text))); context.getConfigurationService().save(setting); } }; limitField.setWidth("5em"); limitField.setValue(context.getUser().get(UserSetting.SETTING_MAX_LOG_MESSAGE_TO_SHOW, "1000")); right.addComponent(limitField); right.setComponentAlignment(limitField, Alignment.MIDDLE_RIGHT); showDiagramCheckbox = new CheckBox("Show Diagram"); showDiagramCheckbox.addValueChangeListener((event) -> { if (showDiagramCheckbox.getValue()) { showDiagram(); } else { showDetails(); } }); right.addComponent(showDiagramCheckbox); right.setComponentAlignment(showDiagramCheckbox, Alignment.MIDDLE_RIGHT); addComponent(topBar); ButtonBar buttonBar = new ButtonBar(); rerunButton = buttonBar.addButton("Rerun", Icons.RUN, event -> rerun()); rerunButton.setVisible(false); removeButton = buttonBar.addButton("Remove", Icons.DELETE, event -> remove()); removeButton.setVisible(false); cancelButton = buttonBar.addButton("Cancel", Icons.CANCEL, event -> cancel()); addComponent(buttonBar); HorizontalLayout header1 = new HorizontalLayout(); header1.addComponent(new Label("<b>Flow:</b>", ContentMode.HTML)); header1.addComponent(flowLabel); header1.addComponent(new Label("<b>Start:</b>", ContentMode.HTML)); header1.addComponent(startLabel); header1.setSpacing(true); header1.setMargin(new MarginInfo(false, true, false, true)); header1.setWidth("100%"); addComponent(header1); HorizontalLayout header2 = new HorizontalLayout(); header2.addComponent(new Label("<b>Status:</b>", ContentMode.HTML)); header2.addComponent(statusLabel); header2.addComponent(new Label("<b>End:</b>", ContentMode.HTML)); header2.addComponent(endLabel); header2.setSpacing(true); header2.setMargin(new MarginInfo(false, true, true, true)); header2.setWidth("100%"); addComponent(header2); stepContainer.setBeanIdProperty("id"); diagramLayout = new VerticalLayout(); diagramLayout.setWidth(10000, Unit.PIXELS); diagramLayout.setHeight(10000, Unit.PIXELS); flowPanel = new Panel(); flowPanel.setSizeFull(); flowPanel.addStyleName(ValoTheme.PANEL_WELL); flowPanel.setContent(diagramLayout); stepTable.setContainerDataSource(stepContainer); stepTable.setSelectable(true); stepTable.setMultiSelect(true); stepTable.setImmediate(true); stepTable.setSizeFull(); stepTable.setVisibleColumns( new Object[] { "componentName", "threadNumber", "status", "payloadReceived", "messagesReceived", "messagesProduced", "payloadProduced", "startTime", "endTime", "handleDurationString" }); stepTable.setColumnHeaders(new String[] { "Component Name", "Thread", "Status", "Payload Recvd", "Msgs Recvd", "Msgs Sent", "Payload Sent", "Start", "End", "Run Duration" }); stepTable.setColumnWidth("status", 100); stepTable.setColumnWidth("messagesReceived", 100); stepTable.setColumnWidth("messagesProduced", 100); stepTable.setColumnWidth("payloadReceived", 100); stepTable.setColumnWidth("payloadProduced", 100); stepTable.setColumnWidth("threadNumber", 100); stepTable.setColumnWidth("startTime", 170); stepTable.setColumnWidth("endTime", 170); stepTable.setColumnExpandRatio("handleDurationString", 1); stepTable.addValueChangeListener(event -> { @SuppressWarnings("unchecked") Set<String> executionStepIds = (Set<String>) event.getProperty().getValue(); logContainer.removeAllItems(); List<ExecutionStepLog> logs = executionService.findExecutionStepLogs(executionStepIds, getMaxToShow()); logContainer.addAll(logs); }); stepTable.addValueChangeListener(event -> { @SuppressWarnings("unchecked") Set<String> executionStepIds = (Set<String>) event.getProperty().getValue(); logContainer.removeAllItems(); List<ExecutionStepLog> logs = executionService.findExecutionStepLogs(executionStepIds, getMaxToShow()); logContainer.addAll(logs); }); logTable = new Grid(); logTable.addColumn("level", String.class).setHeaderCaption("Level").setWidth(110).setMaximumWidth(200); logTable.addColumn("createTime", Date.class).setHeaderCaption("Time").setWidth(120).setMaximumWidth(200) .setRenderer(new DateRenderer(UiConstants.TIME_FORMAT)); logTable.addColumn("logText", String.class).setHeaderCaption("Message").setExpandRatio(1); logTable.setContainerDataSource(logContainer); logTable.setSizeFull(); logTable.addItemClickListener(event -> logTableCellClicked(logTable, event)); logTable.addSortListener(event -> { lastSortOrder = event.getSortOrder(); }); HeaderRow filteringHeader = logTable.appendHeaderRow(); HeaderCell logTextFilterCell = filteringHeader.getCell("logText"); TextField filterField = new TextField(); filterField.setInputPrompt("Filter"); filterField.addStyleName(ValoTheme.TEXTFIELD_TINY); filterField.setWidth("100%"); // Update filter When the filter input is changed filterField.addTextChangeListener(change -> { // Can't modify filters so need to replace logContainer.removeContainerFilters("logText"); // (Re)create the filter if necessary if (!change.getText().isEmpty()) logContainer.addContainerFilter(new SimpleStringFilter("logText", change.getText(), true, false)); }); logTextFilterCell.setComponent(filterField); HeaderCell levelFilterCell = filteringHeader.getCell("level"); ComboBox levelFilter = new ComboBox(); levelFilter.setWidth(8, Unit.EM); levelFilter.setNullSelectionAllowed(true); LogLevel[] levels = LogLevel.values(); for (LogLevel logLevel : levels) { levelFilter.addItem(logLevel.name()); } levelFilter.addValueChangeListener(change -> { logContainer.removeContainerFilters("level"); String text = (String) levelFilter.getValue(); if (isNotBlank(text)) { logContainer.addContainerFilter(new SimpleStringFilter("level", text, true, false)); } }); levelFilterCell.setComponent(levelFilter); levelFilter.addStyleName(ValoTheme.COMBOBOX_TINY); splitPanel = new VerticalSplitPanel(); splitPanel.setFirstComponent(flowPanel); splitPanel.setSecondComponent(logTable); splitPanel.setSplitPosition(50f); splitPanel.setSizeFull(); addComponent(splitPanel); setExpandRatio(splitPanel, 1.0f); showDiagramCheckbox.setValue(context.getUser().getBoolean(UserSetting.SETTING_SHOW_RUN_DIAGRAM, true)); if (!showDiagramCheckbox.getValue()) { showDetails(); } context.getBackgroundRefresherService().register(this); } protected void redrawFlow() { if (diagram != null) { diagramLayout.removeComponent(diagram); } diagram = new RunDiagram(); diagram.setSizeFull(); diagram.addListener(new RunDiagramChangedListener()); diagram.setNodes(getNodes()); diagramLayout.addComponent(diagram); } protected List<Node> getNodes() { ExecutionData executionData = getExecutionData(); List<FlowStep> flowSteps = flow.getFlowSteps(); List<FlowStepLink> links = flow.getFlowStepLinks(); List<Node> list = new ArrayList<Node>(); int activeSteps = 0; for (FlowStep step : flowSteps) { if (step.getComponent().getBoolean(AbstractComponentRuntime.ENABLED, true)) { activeSteps++; } } // If the execution steps don't match the flow steps, wait and try // again. // The execution log steps may not be persisted yet. for (int i = 0; i < 5; i++) { if (executionData.steps.size() == activeSteps) { break; } else { AppUtils.sleep(200); executionData = getExecutionData(); } } for (FlowStep flowStep : flowSteps) { Node node = new Node(); String name = flowStep.getComponent().getName(); String type = flowStep.getComponent().getType(); boolean enabled = flowStep.getComponent().getBoolean(AbstractComponentRuntime.ENABLED, true); String imageText = String.format( "<img style=\"display: block; margin-left: auto; margin-right: auto\" src=\"data:image/png;base64,%s\"/>", UiUtils.getBase64RepresentationOfImageForComponentType(type, context)); node.setText(imageText); node.setName(name); node.setEnabled(enabled); node.setId(flowStep.getId()); node.setX(flowStep.getX()); node.setY(flowStep.getY()); ExecutionStep executionStep = executionData.findExecutionStep(flowStep.getId()); if (node.isEnabled() && executionStep != null) { node.setEntitiesProcessed(executionStep.getEntitiesProcessed()); node.setMessagesRecieved(executionStep.getMessagesReceived()); node.setMessagesSent(executionStep.getMessagesProduced()); node.setStatus(executionStep.getExecutionStatus().toString()); node.setInputLabel(Long.toString(executionStep.getMessagesReceived())); node.setOutputLabel(Long.toString(executionStep.getMessagesProduced())); } else if (!node.isEnabled()) { node.setInputLabel("-"); node.setOutputLabel("-"); } else { // Show the detail screen if the flow does not match the // historical execution. showDetails(); showDiagramCheckbox.setEnabled(false); showDiagramCheckbox.setDescription( "The flow has been modified since the execution. The flow cannot be viewed."); } for (FlowStepLink link : links) { if (link.getSourceStepId().equals(node.getId())) { node.getTargetNodeIds().add(link.getTargetStepId()); } } list.add(node); } return list; } protected void logTableCellClicked(Grid logTable, ItemClickEvent event) { if (event.isDoubleClick()) { Object object = event.getPropertyId(); if (!object.toString().equals("")) { Object prop = event.getPropertyId(); String header = logTable.getColumn(prop).getHeaderCaption(); Property<?> p = event.getItem().getItemProperty(prop); if (p != null) { String data = String.valueOf(p.getValue()); new ReadOnlyTextAreaDialog(header, data, false).showAtSize(.5); } } } } @Override public boolean closing() { context.getBackgroundRefresherService().unregister(this); return true; } @Override public void selected() { } @Override public void deselected() { } protected void rerun() { parentTabSheet.closeTab(executionId); flowRunnable.runFlow(); } protected void remove() { ConfirmDialog.show("Delete Execution?", "Are you sure you want to delete this execution?", () -> { context.getExecutionService().deleteExecution(executionId); parentTabSheet.closeTab(executionId); return true; }); } protected void cancel() { ConfirmDialog.show("Cancel Execution?", "Are you sure you want to cancel this execution?", () -> { context.getAgentManager().cancel(executionId); cancelButton.setEnabled(false); return true; }); } protected void showDiagram() { splitPanel.setFirstComponent(flowPanel); Setting setting = context.getUser().findSetting(UserSetting.SETTING_SHOW_RUN_DIAGRAM); setting.setValue("true"); context.getConfigurationService().save(setting); redrawFlow(); } protected void showDetails() { splitPanel.setFirstComponent(stepTable); Setting setting = context.getUser().findSetting(UserSetting.SETTING_SHOW_RUN_DIAGRAM); setting.setValue("false"); context.getConfigurationService().save(setting); } @SuppressWarnings("unchecked") @Override public Object onBackgroundDataRefresh() { if (!lastDataRefreshWasDone) { return getExecutionData(); } else { return null; } } @Override public void onBackgroundUIRefresh(Object backgroundData) { if (backgroundData != null) { refreshUI((ExecutionData) backgroundData); } } @SuppressWarnings("unchecked") protected ExecutionData getExecutionData() { ExecutionData data = new ExecutionData(); data.execution = executionService.findExecution(executionId); data.steps = executionService.findExecutionSteps(executionId); this.flow = context.getConfigurationService().findFlow(data.execution.getFlowId()); Set<String> selected = (Set<String>) stepTable.getValue(); data.logs = executionService.findExecutionStepLogs(selected, getMaxToShow()); return data; } protected int getMaxToShow() { return getMaxToShow(limitField.getValue()); } protected int getMaxToShow(String text) { try { return Integer.parseInt(text); } catch (Exception e) { return 100; } } class RunDiagramChangedListener implements Listener { private static final long serialVersionUID = 1L; @Override public void componentEvent(Event e) { if (e instanceof NodeSelectedEvent) { NodeSelectedEvent event = (NodeSelectedEvent) e; List<String> nodeIds = event.getNodeIds(); ExecutionData data = getExecutionData(); Set<String> stepIds = new HashSet<String>(nodeIds.size()); for (String id : nodeIds) { ExecutionStep step = data.findExecutionStep(id); if (step != null) { stepIds.add(step.getId()); } } logContainer.removeAllItems(); List<ExecutionStepLog> logs = executionService.findExecutionStepLogs(stepIds, getMaxToShow()); logContainer.addAll(logs); } } } protected boolean isDone() { return ExecutionStatus.isDone(statusLabel.getValue()); } protected void refreshUI(ExecutionData data) { if (!lastDataRefreshWasDone) { flowLabel.setValue(data.execution.getFlowName()); startLabel.setValue(formatDate(data.execution.getStartTime())); if (data.execution.getStatus() != null) { if (data.execution.getStatus().equals(ExecutionStatus.ERROR.name())) { statusLabel.setStyleName("error"); statusLabel.setValue(FontAwesome.WARNING.getHtml() + " " + data.execution.getStatus()); } else if (data.execution.getStatus().equals(ExecutionStatus.DONE.name())) { statusLabel.setStyleName("done"); statusLabel.setValue(FontAwesome.CHECK.getHtml() + " " + data.execution.getStatus()); } else if (data.execution.getStatus().equals(ExecutionStatus.RUNNING.name())) { statusLabel.setStyleName("running"); statusLabel.setValue(FontAwesome.SPINNER.getHtml() + " " + data.execution.getStatus()); } else { statusLabel.setStyleName(""); statusLabel.setValue(data.execution.getStatus()); } } endLabel.setValue(formatDate(data.execution.getEndTime())); if (showDiagramCheckbox.getValue()) { redrawFlow(); } @SuppressWarnings("unchecked") Set<String> selected = (Set<String>) stepTable.getValue(); stepContainer.removeAllItems(); stepContainer.addAll(data.steps); if (selected != null && selected.size() > 0) { stepTable.setValue(selected); } else if (data.steps.size() > 0) { selected = new HashSet<>(); selected.add(data.steps.get(0).getId()); stepTable.setValue(selected); } List<ExecutionStepLog> logMessages = new ArrayList<>(logContainer.getItemIds()); List<ExecutionStepLog> newLogMessages = new ArrayList<>(data.logs); for (ExecutionStepLog logMsg : logMessages) { newLogMessages.remove(logMsg); } if (newLogMessages.size() > 0) { logContainer.addAll(newLogMessages); if (lastSortOrder != null) { logTable.setSortOrder(lastSortOrder); } } } rerunButton.setVisible(isDone() && flowRunnable != null); removeButton.setVisible(isDone()); cancelButton.setVisible(!isDone()); lastDataRefreshWasDone = isDone(); } protected String formatDate(Date date) { SimpleDateFormat df = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss aa"); if (date != null) { return df.format(date); } return ""; } public class ExecutionData { public Execution execution; public List<ExecutionStep> steps; public List<ExecutionStepLog> logs; ExecutionStep findExecutionStep(String id) { ExecutionStep executionStep = null; for (ExecutionStep s : steps) { if (s.getFlowStepId().equals(id)) { executionStep = s; break; } } return executionStep; } } public class ComponentNameColumnGenerator implements ColumnGenerator { private static final long serialVersionUID = 1L; @SuppressWarnings("unchecked") public Object generateCell(com.vaadin.ui.Table source, Object itemId, Object columnId) { BeanItem<ExecutionStepLog> logItem = (BeanItem<ExecutionStepLog>) source.getItem(itemId); String executionStepId = (String) logItem.getItemProperty("executionStepId").getValue(); BeanItem<ExecutionStep> stepItem = stepContainer.getItem(executionStepId); return new Label((String) stepItem.getItemProperty("componentName").getValue()); } } }