/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2013, MPL CodeInside http://codeinside.ru
*/
package ru.codeinside.gses.webui.components;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.vaadin.Application;
import com.vaadin.terminal.StreamResource;
import com.vaadin.terminal.StreamResource.StreamSource;
import com.vaadin.ui.Embedded;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.runtime.Execution;
import ru.codeinside.gses.service.F1;
import ru.codeinside.gses.service.Fn;
import ru.codeinside.gses.service.Functions;
import ru.codeinside.gses.webui.Flash;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
//TODO: хранить изображение в во временом файле, кеширование по названию между сессиями.
final public class TaskGraph extends Embedded {
final static Joiner ID_JOINER = Joiner.on('/').skipNulls();
final static Joiner FILE_JOINER = Joiner.on('_').skipNulls();
final public boolean hasBlocks;
public TaskGraph(final String processDefinitionId, final String executionId) {
final List<String> activeActivityIds;
final String baseName;
final String appendix;
if (executionId != null) {
baseName = executionId;
activeActivityIds = Fn.withEngine(new ActiveActivityIds(), executionId);
if (activeActivityIds.isEmpty()) {
appendix = null;
hasBlocks = false;
} else {
hasBlocks = true;
appendix = getSHA(ID_JOINER.join(activeActivityIds));
}
} else {
activeActivityIds = Collections.emptyList();
baseName = processDefinitionId.replace(':', '_');
appendix = null;
hasBlocks = false;
}
final String filename = FILE_JOINER.join("ex", baseName, appendix) + ".png";
setMimeType("image/png");
setSource(
new AppCachedStreamSource(
new GraphStreamSource(new GraphProducer(processDefinitionId, activeActivityIds)),
filename,
Flash.app()
)
);
}
final static class AppCachedStreamSource extends StreamResource {
public AppCachedStreamSource(final StreamSource streamSource, final String filename, final Application application) {
super(streamSource, filename, application);
}
@Override
public int hashCode() {
return getFilename().hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AppCachedStreamSource)) {
return false;
}
return getFilename().equals(((AppCachedStreamSource) obj).getFilename());
}
}
static String getSHA(final String string) {
try {
final MessageDigest md = MessageDigest.getInstance("SHA");
md.update(string.getBytes("UTF8"));
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
return "" + string.hashCode();
} catch (UnsupportedEncodingException e) {
return "" + string.hashCode();
}
}
final static class GraphStreamSource implements StreamSource {
final private GraphProducer graphProducer;
public GraphStreamSource(final GraphProducer graphProducer) {
this.graphProducer = graphProducer;
}
public InputStream getStream() {
return Functions.withRepository(Flash.login(), graphProducer);
}
}
final static class ActiveActivityIds implements F1<List<String>, String> {
@Override
public List<String> apply(ProcessEngine engine, final String executionId) {
// может уже выполниться!
final Execution execution = engine.getRuntimeService().createExecutionQuery().executionId(executionId).singleResult();
if (execution == null) {
return Collections.emptyList();
}
List<String> ids = engine.getRuntimeService().getActiveActivityIds(executionId);
if (!ids.isEmpty()) {
return ids;
}
return Arrays.asList(((ExecutionEntity) execution).getCurrentActivityId());
}
}
final static class GraphProducer implements Function<RepositoryService, InputStream>, Serializable {
private final String processDefinitionId;
private final List<String> activeActivityIds;
public GraphProducer(final String processDefinitionId, final List<String> activeActivityIds) {
this.processDefinitionId = processDefinitionId;
this.activeActivityIds = activeActivityIds;
}
final public InputStream apply(final RepositoryService repositoryService) {
final RepositoryServiceImpl impl = (RepositoryServiceImpl) repositoryService;
final ProcessDefinitionEntity def = (ProcessDefinitionEntity) impl.getDeployedProcessDefinition(processDefinitionId);
return ProcessDiagramGenerator.generateDiagram(def, "png", activeActivityIds);
}
}
}