/* 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.engine.impl.bpmn.deployer; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.activiti.bpmn.constants.BpmnXMLConstants; import org.activiti.bpmn.model.BpmnModel; import org.activiti.bpmn.model.ExtensionElement; import org.activiti.bpmn.model.FlowElement; import org.activiti.bpmn.model.Process; import org.activiti.bpmn.model.SubProcess; import org.activiti.bpmn.model.UserTask; import org.activiti.bpmn.model.ValuedDataObject; import org.activiti.engine.ActivitiException; import org.activiti.engine.DynamicBpmnConstants; import org.activiti.engine.DynamicBpmnService; import org.activiti.engine.ProcessEngineConfiguration; import org.activiti.engine.delegate.Expression; import org.activiti.engine.delegate.event.ActivitiEventType; import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder; import org.activiti.engine.impl.bpmn.parser.BpmnParse; import org.activiti.engine.impl.bpmn.parser.BpmnParser; import org.activiti.engine.impl.bpmn.parser.EventSubscriptionDeclaration; import org.activiti.engine.impl.cfg.IdGenerator; import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti.engine.impl.cmd.CancelJobsCmd; import org.activiti.engine.impl.cmd.DeploymentSettings; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.db.DbSqlSession; import org.activiti.engine.impl.el.ExpressionManager; import org.activiti.engine.impl.event.MessageEventHandler; import org.activiti.engine.impl.event.SignalEventHandler; import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl; import org.activiti.engine.impl.jobexecutor.TimerStartEventJobHandler; import org.activiti.engine.impl.persistence.deploy.Deployer; import org.activiti.engine.impl.persistence.deploy.DeploymentManager; import org.activiti.engine.impl.persistence.deploy.ProcessDefinitionInfoCacheObject; import org.activiti.engine.impl.persistence.entity.DeploymentEntity; import org.activiti.engine.impl.persistence.entity.EventSubscriptionEntity; import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity; import org.activiti.engine.impl.persistence.entity.MessageEventSubscriptionEntity; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntityManager; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionInfoEntity; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionInfoEntityManager; import org.activiti.engine.impl.persistence.entity.ResourceEntity; import org.activiti.engine.impl.persistence.entity.SignalEventSubscriptionEntity; import org.activiti.engine.impl.persistence.entity.TimerEntity; import org.activiti.engine.impl.util.IoUtil; import org.activiti.engine.runtime.Job; import org.activiti.engine.task.IdentityLinkType; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; /** * @author Tom Baeyens * @author Joram Barrez */ public class BpmnDeployer implements Deployer { private static final Logger log = LoggerFactory.getLogger(BpmnDeployer.class); public static final String[] BPMN_RESOURCE_SUFFIXES = new String[] { "bpmn20.xml", "bpmn" }; public static final String[] DIAGRAM_SUFFIXES = new String[]{"png", "jpg", "gif", "svg"}; protected ExpressionManager expressionManager; protected BpmnParser bpmnParser; protected IdGenerator idGenerator; public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) { log.debug("Processing deployment {}", deployment.getName()); List<ProcessDefinitionEntity> processDefinitions = new ArrayList<ProcessDefinitionEntity>(); Map<String, ResourceEntity> resources = deployment.getResources(); Map<String, BpmnModel> bpmnModelMap = new HashMap<String, BpmnModel>(); final ProcessEngineConfigurationImpl processEngineConfiguration = Context.getProcessEngineConfiguration(); for (String resourceName : resources.keySet()) { log.info("Processing resource {}", resourceName); if (isBpmnResource(resourceName)) { ResourceEntity resource = resources.get(resourceName); byte[] bytes = resource.getBytes(); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); BpmnParse bpmnParse = bpmnParser .createParse() .sourceInputStream(inputStream) .setSourceSystemId(resourceName) .deployment(deployment) .name(resourceName); if (deploymentSettings != null) { // Schema validation if needed if (deploymentSettings.containsKey(DeploymentSettings.IS_BPMN20_XSD_VALIDATION_ENABLED)) { bpmnParse.setValidateSchema((Boolean) deploymentSettings.get(DeploymentSettings.IS_BPMN20_XSD_VALIDATION_ENABLED)); } // Process validation if needed if (deploymentSettings.containsKey(DeploymentSettings.IS_PROCESS_VALIDATION_ENABLED)) { bpmnParse.setValidateProcess((Boolean) deploymentSettings.get(DeploymentSettings.IS_PROCESS_VALIDATION_ENABLED)); } } else { // On redeploy, we assume it is validated at the first deploy bpmnParse.setValidateSchema(false); bpmnParse.setValidateProcess(false); } bpmnParse.execute(); for (ProcessDefinitionEntity processDefinition: bpmnParse.getProcessDefinitions()) { processDefinition.setResourceName(resourceName); if (deployment.getTenantId() != null) { processDefinition.setTenantId(deployment.getTenantId()); // process definition inherits the tenant id } String diagramResourceName = getDiagramResourceForProcess(resourceName, processDefinition.getKey(), resources); // Only generate the resource when deployment is new to prevent modification of deployment resources // after the process-definition is actually deployed. Also to prevent resource-generation failure every // time the process definition is added to the deployment-cache when diagram-generation has failed the first time. if(deployment.isNew()) { if (processEngineConfiguration.isCreateDiagramOnDeploy() && diagramResourceName==null && processDefinition.isGraphicalNotationDefined()) { try { byte[] diagramBytes = IoUtil.readInputStream(processEngineConfiguration. getProcessDiagramGenerator().generateDiagram(bpmnParse.getBpmnModel(), "png", processEngineConfiguration.getActivityFontName(), processEngineConfiguration.getLabelFontName(),processEngineConfiguration.getAnnotationFontName(), processEngineConfiguration.getClassLoader()), null); diagramResourceName = getProcessImageResourceName(resourceName, processDefinition.getKey(), "png"); createResource(diagramResourceName, diagramBytes, deployment); } catch (Throwable t) { // if anything goes wrong, we don't store the image (the process will still be executable). log.warn("Error while generating process diagram, image will not be stored in repository", t); } } } processDefinition.setDiagramResourceName(diagramResourceName); processDefinitions.add(processDefinition); bpmnModelMap.put(processDefinition.getKey(), bpmnParse.getBpmnModel()); } } } // check if there are process definitions with the same process key to prevent database unique index violation List<String> keyList = new ArrayList<String>(); for (ProcessDefinitionEntity processDefinition : processDefinitions) { if (keyList.contains(processDefinition.getKey())) { throw new ActivitiException("The deployment contains process definitions with the same key '"+ processDefinition.getKey() +"' (process id atrribute), this is not allowed"); } keyList.add(processDefinition.getKey()); } CommandContext commandContext = Context.getCommandContext(); ProcessDefinitionEntityManager processDefinitionManager = commandContext.getProcessDefinitionEntityManager(); DbSqlSession dbSqlSession = commandContext.getSession(DbSqlSession.class); for (ProcessDefinitionEntity processDefinition : processDefinitions) { List<TimerEntity> timers = new ArrayList<TimerEntity>(); if (deployment.isNew()) { int processDefinitionVersion; ProcessDefinitionEntity latestProcessDefinition = null; if (processDefinition.getTenantId() != null && !ProcessEngineConfiguration.NO_TENANT_ID.equals(processDefinition.getTenantId())) { latestProcessDefinition = processDefinitionManager .findLatestProcessDefinitionByKeyAndTenantId(processDefinition.getKey(), processDefinition.getTenantId()); } else { latestProcessDefinition = processDefinitionManager .findLatestProcessDefinitionByKey(processDefinition.getKey()); } if (latestProcessDefinition != null) { processDefinitionVersion = latestProcessDefinition.getVersion() + 1; } else { processDefinitionVersion = 1; } processDefinition.setVersion(processDefinitionVersion); processDefinition.setDeploymentId(deployment.getId()); String nextId = idGenerator.getNextId(); String processDefinitionId = processDefinition.getKey() + ":" + processDefinition.getVersion() + ":" + nextId; // ACT-505 // ACT-115: maximum id length is 64 charcaters if (processDefinitionId.length() > 64) { processDefinitionId = nextId; } processDefinition.setId(processDefinitionId); if(commandContext.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) { commandContext.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent( ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, processDefinition)); } removeObsoleteTimers(processDefinition); addTimerDeclarations(processDefinition, timers); removeExistingMessageEventSubscriptions(processDefinition, latestProcessDefinition); addMessageEventSubscriptions(processDefinition); removeExistingSignalEventSubScription(processDefinition, latestProcessDefinition); addSignalEventSubscriptions(processDefinition); dbSqlSession.insert(processDefinition); addAuthorizations(processDefinition); if(commandContext.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) { commandContext.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent( ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_INITIALIZED, processDefinition)); } scheduleTimers(timers); } else { String deploymentId = deployment.getId(); processDefinition.setDeploymentId(deploymentId); ProcessDefinitionEntity persistedProcessDefinition = null; if (processDefinition.getTenantId() == null || ProcessEngineConfiguration.NO_TENANT_ID.equals(processDefinition.getTenantId())) { persistedProcessDefinition = processDefinitionManager.findProcessDefinitionByDeploymentAndKey(deploymentId, processDefinition.getKey()); } else { persistedProcessDefinition = processDefinitionManager.findProcessDefinitionByDeploymentAndKeyAndTenantId(deploymentId, processDefinition.getKey(), processDefinition.getTenantId()); } if (persistedProcessDefinition != null) { processDefinition.setId(persistedProcessDefinition.getId()); processDefinition.setVersion(persistedProcessDefinition.getVersion()); processDefinition.setSuspensionState(persistedProcessDefinition.getSuspensionState()); } } // Add to cache DeploymentManager deploymentManager = processEngineConfiguration.getDeploymentManager(); deploymentManager.getProcessDefinitionCache().add(processDefinition.getId(), processDefinition); addDefinitionInfoToCache(processDefinition, processEngineConfiguration, commandContext); // Add to deployment for further usage deployment.addDeployedArtifact(processDefinition); createLocalizationValues(processDefinition.getId(), bpmnModelMap.get(processDefinition.getKey()).getProcessById(processDefinition.getKey())); } } protected void addDefinitionInfoToCache(ProcessDefinitionEntity processDefinition, ProcessEngineConfigurationImpl processEngineConfiguration, CommandContext commandContext) { if (processEngineConfiguration.isEnableProcessDefinitionInfoCache() == false) { return; } DeploymentManager deploymentManager = processEngineConfiguration.getDeploymentManager(); ProcessDefinitionInfoEntityManager definitionInfoEntityManager = commandContext.getProcessDefinitionInfoEntityManager(); ObjectMapper objectMapper = commandContext.getProcessEngineConfiguration().getObjectMapper(); ProcessDefinitionInfoEntity definitionInfoEntity = definitionInfoEntityManager.findProcessDefinitionInfoByProcessDefinitionId(processDefinition.getId()); ObjectNode infoNode = null; if (definitionInfoEntity != null && definitionInfoEntity.getInfoJsonId() != null) { byte[] infoBytes = definitionInfoEntityManager.findInfoJsonById(definitionInfoEntity.getInfoJsonId()); if (infoBytes != null) { try { infoNode = (ObjectNode) objectMapper.readTree(infoBytes); } catch (Exception e) { throw new ActivitiException("Error deserializing json info for process definition " + processDefinition.getId()); } } } ProcessDefinitionInfoCacheObject definitionCacheObject = new ProcessDefinitionInfoCacheObject(); if (definitionInfoEntity == null) { definitionCacheObject.setRevision(0); } else { definitionCacheObject.setId(definitionInfoEntity.getId()); definitionCacheObject.setRevision(definitionInfoEntity.getRevision()); } if (infoNode == null) { infoNode = objectMapper.createObjectNode(); } definitionCacheObject.setInfoNode(infoNode); deploymentManager.getProcessDefinitionInfoCache().add(processDefinition.getId(), definitionCacheObject); } protected void scheduleTimers(List<TimerEntity> timers) { for (TimerEntity timer : timers) { Context .getCommandContext() .getJobEntityManager() .schedule(timer); } } @SuppressWarnings("unchecked") protected void addTimerDeclarations(ProcessDefinitionEntity processDefinition, List<TimerEntity> timers) { List<TimerDeclarationImpl> timerDeclarations = (List<TimerDeclarationImpl>) processDefinition.getProperty(BpmnParse.PROPERTYNAME_START_TIMER); if (timerDeclarations!=null) { for (TimerDeclarationImpl timerDeclaration : timerDeclarations) { TimerEntity timer = timerDeclaration.prepareTimerEntity(null); if (timer!=null) { timer.setProcessDefinitionId(processDefinition.getId()); // Inherit timer (if appliccable) if (processDefinition.getTenantId() != null) { timer.setTenantId(processDefinition.getTenantId()); } timers.add(timer); } } } } protected void removeObsoleteTimers(ProcessDefinitionEntity processDefinition) { List<Job> jobsToDelete = null; if (processDefinition.getTenantId() != null && !ProcessEngineConfiguration.NO_TENANT_ID.equals(processDefinition.getTenantId())) { jobsToDelete = Context.getCommandContext().getJobEntityManager().findJobsByTypeAndProcessDefinitionKeyAndTenantId( TimerStartEventJobHandler.TYPE, processDefinition.getKey(), processDefinition.getTenantId()); } else { jobsToDelete = Context.getCommandContext().getJobEntityManager() .findJobsByTypeAndProcessDefinitionKeyNoTenantId(TimerStartEventJobHandler.TYPE, processDefinition.getKey()); } if (jobsToDelete != null) { for (Job job :jobsToDelete) { new CancelJobsCmd(job.getId()).execute(Context.getCommandContext()); } } } protected void removeExistingMessageEventSubscriptions(ProcessDefinitionEntity processDefinition, ProcessDefinitionEntity latestProcessDefinition) { if(latestProcessDefinition != null) { CommandContext commandContext = Context.getCommandContext(); List<EventSubscriptionEntity> subscriptionsToDisable = commandContext .getEventSubscriptionEntityManager() .findEventSubscriptionsByTypeAndProcessDefinitionId(MessageEventHandler.EVENT_HANDLER_TYPE, latestProcessDefinition.getId(), latestProcessDefinition.getTenantId()); for (EventSubscriptionEntity eventSubscriptionEntity : subscriptionsToDisable) { eventSubscriptionEntity.delete(); } } } @SuppressWarnings("unchecked") protected void addMessageEventSubscriptions(ProcessDefinitionEntity processDefinition) { CommandContext commandContext = Context.getCommandContext(); List<EventSubscriptionDeclaration> eventDefinitions = (List<EventSubscriptionDeclaration>) processDefinition.getProperty(BpmnParse.PROPERTYNAME_EVENT_SUBSCRIPTION_DECLARATION); if(eventDefinitions != null) { Set<String> messageNames = new HashSet<String>(); for (EventSubscriptionDeclaration eventDefinition : eventDefinitions) { if(eventDefinition.getEventType().equals("message") && eventDefinition.isStartEvent()) { if (!messageNames.contains(eventDefinition.getEventName())) { messageNames.add(eventDefinition.getEventName()); } else { throw new ActivitiException("Cannot deploy process definition '" + processDefinition.getResourceName() + "': there are multiple message event subscriptions for the message with name '" + eventDefinition.getEventName() + "'."); } // look for subscriptions for the same name in db: List<EventSubscriptionEntity> subscriptionsForSameMessageName = commandContext .getEventSubscriptionEntityManager() .findEventSubscriptionsByName(MessageEventHandler.EVENT_HANDLER_TYPE, eventDefinition.getEventName(), processDefinition.getTenantId()); // also look for subscriptions created in the session: List<MessageEventSubscriptionEntity> cachedSubscriptions = commandContext .getDbSqlSession() .findInCache(MessageEventSubscriptionEntity.class); for (MessageEventSubscriptionEntity cachedSubscription : cachedSubscriptions) { if(eventDefinition.getEventName().equals(cachedSubscription.getEventName()) && !subscriptionsForSameMessageName.contains(cachedSubscription)) { subscriptionsForSameMessageName.add(cachedSubscription); } } // remove subscriptions deleted in the same command subscriptionsForSameMessageName = commandContext .getDbSqlSession() .pruneDeletedEntities(subscriptionsForSameMessageName); for (EventSubscriptionEntity eventSubscriptionEntity : subscriptionsForSameMessageName) { // throw exception only if there's already a subscription as start event // no process instance-id = it's a message start event if(StringUtils.isEmpty(eventSubscriptionEntity.getProcessInstanceId())) { throw new ActivitiException("Cannot deploy process definition '" + processDefinition.getResourceName() + "': there already is a message event subscription for the message with name '" + eventDefinition.getEventName() + "'."); } } MessageEventSubscriptionEntity newSubscription = new MessageEventSubscriptionEntity(); newSubscription.setEventName(eventDefinition.getEventName()); newSubscription.setActivityId(eventDefinition.getActivityId()); newSubscription.setConfiguration(processDefinition.getId()); newSubscription.setProcessDefinitionId(processDefinition.getId()); if (processDefinition.getTenantId() != null) { newSubscription.setTenantId(processDefinition.getTenantId()); } newSubscription.insert(); } } } } protected void removeExistingSignalEventSubScription(ProcessDefinitionEntity processDefinition, ProcessDefinitionEntity latestProcessDefinition) { if(latestProcessDefinition != null) { CommandContext commandContext = Context.getCommandContext(); List<EventSubscriptionEntity> subscriptionsToDisable = commandContext .getEventSubscriptionEntityManager() .findEventSubscriptionsByTypeAndProcessDefinitionId(SignalEventHandler.EVENT_HANDLER_TYPE, latestProcessDefinition.getId(), latestProcessDefinition.getTenantId()); for (EventSubscriptionEntity eventSubscriptionEntity : subscriptionsToDisable) { eventSubscriptionEntity.delete(); } } } @SuppressWarnings("unchecked") protected void addSignalEventSubscriptions(ProcessDefinitionEntity processDefinition) { List<EventSubscriptionDeclaration> eventDefinitions = (List<EventSubscriptionDeclaration>) processDefinition.getProperty(BpmnParse.PROPERTYNAME_EVENT_SUBSCRIPTION_DECLARATION); if(eventDefinitions != null) { for (EventSubscriptionDeclaration eventDefinition : eventDefinitions) { if(eventDefinition.getEventType().equals("signal") && eventDefinition.isStartEvent()) { SignalEventSubscriptionEntity subscriptionEntity = new SignalEventSubscriptionEntity(); subscriptionEntity.setEventName(eventDefinition.getEventName()); subscriptionEntity.setActivityId(eventDefinition.getActivityId()); subscriptionEntity.setProcessDefinitionId(processDefinition.getId()); if (processDefinition.getTenantId() != null) { subscriptionEntity.setTenantId(processDefinition.getTenantId()); } subscriptionEntity.insert(); } } } } protected void createLocalizationValues(String processDefinitionId, Process process) { if (process == null) return; CommandContext commandContext = Context.getCommandContext(); DynamicBpmnService dynamicBpmnService = commandContext.getProcessEngineConfiguration().getDynamicBpmnService(); ObjectNode infoNode = dynamicBpmnService.getProcessDefinitionInfo(processDefinitionId); boolean localizationValuesChanged = false; List<ExtensionElement> localizationElements = process.getExtensionElements().get("localization"); if (localizationElements != null) { for (ExtensionElement localizationElement : localizationElements) { if (BpmnXMLConstants.ACTIVITI_EXTENSIONS_PREFIX.equals(localizationElement.getNamespacePrefix())) { String locale = localizationElement.getAttributeValue(null, "locale"); String name = localizationElement.getAttributeValue(null, "name"); String documentation = null; List<ExtensionElement> documentationElements = localizationElement.getChildElements().get("documentation"); if (documentationElements != null) { for (ExtensionElement documentationElement : documentationElements) { documentation = StringUtils.trimToNull(documentationElement.getElementText()); break; } } String processId = process.getId(); if (isEqualToCurrentLocalizationValue(locale, processId, "name", name, infoNode) == false) { dynamicBpmnService.changeLocalizationName(locale, processId, name, infoNode); localizationValuesChanged = true; } if (documentation != null && isEqualToCurrentLocalizationValue(locale, processId, "description", documentation, infoNode) == false) { dynamicBpmnService.changeLocalizationDescription(locale, processId, documentation, infoNode); localizationValuesChanged = true; } break; } } } boolean isFlowElementLocalizationChanged = localizeFlowElements(process.getFlowElements(), infoNode); boolean isDataObjectLocalizationChanged = localizeDataObjectElements(process.getDataObjects(), infoNode); if (isFlowElementLocalizationChanged || isDataObjectLocalizationChanged) { localizationValuesChanged = true; } if (localizationValuesChanged) { dynamicBpmnService.saveProcessDefinitionInfo(processDefinitionId, infoNode); } } protected boolean localizeFlowElements(Collection<FlowElement> flowElements, ObjectNode infoNode) { boolean localizationValuesChanged = false; if (flowElements == null) return localizationValuesChanged; CommandContext commandContext = Context.getCommandContext(); DynamicBpmnService dynamicBpmnService = commandContext.getProcessEngineConfiguration().getDynamicBpmnService(); for (FlowElement flowElement : flowElements) { if (flowElement instanceof UserTask || flowElement instanceof SubProcess) { List<ExtensionElement> localizationElements = flowElement.getExtensionElements().get("localization"); if (localizationElements != null) { for (ExtensionElement localizationElement : localizationElements) { if (BpmnXMLConstants.ACTIVITI_EXTENSIONS_PREFIX.equals(localizationElement.getNamespacePrefix())) { String locale = localizationElement.getAttributeValue(null, "locale"); String name = localizationElement.getAttributeValue(null, "name"); String documentation = null; List<ExtensionElement> documentationElements = localizationElement.getChildElements().get("documentation"); if (documentationElements != null) { for (ExtensionElement documentationElement : documentationElements) { documentation = StringUtils.trimToNull(documentationElement.getElementText()); break; } } String flowElementId = flowElement.getId(); if (isEqualToCurrentLocalizationValue(locale, flowElementId, "name", name, infoNode) == false) { dynamicBpmnService.changeLocalizationName(locale, flowElementId, name, infoNode); localizationValuesChanged = true; } if (documentation != null && isEqualToCurrentLocalizationValue(locale, flowElementId, "description", documentation, infoNode) == false) { dynamicBpmnService.changeLocalizationDescription(locale, flowElementId, documentation, infoNode); localizationValuesChanged = true; } break; } } } if (flowElement instanceof SubProcess) { SubProcess subprocess = (SubProcess) flowElement; boolean isFlowElementLocalizationChanged = localizeFlowElements(subprocess.getFlowElements(), infoNode); boolean isDataObjectLocalizationChanged = localizeDataObjectElements(subprocess.getDataObjects(), infoNode); if (isFlowElementLocalizationChanged || isDataObjectLocalizationChanged) { localizationValuesChanged = true; } } } } return localizationValuesChanged; } protected boolean isEqualToCurrentLocalizationValue(String language, String id, String propertyName, String propertyValue, ObjectNode infoNode) { boolean isEqual = false; JsonNode localizationNode = infoNode.path("localization").path(language).path(id).path(propertyName); if (localizationNode.isMissingNode() == false && localizationNode.isNull() == false && localizationNode.asText().equals(propertyValue)) { isEqual = true; } return isEqual; } protected boolean localizeDataObjectElements(List<ValuedDataObject> dataObjects, ObjectNode infoNode) { boolean localizationValuesChanged = false; CommandContext commandContext = Context.getCommandContext(); DynamicBpmnService dynamicBpmnService = commandContext.getProcessEngineConfiguration().getDynamicBpmnService(); for(ValuedDataObject dataObject : dataObjects) { List<ExtensionElement> localizationElements = dataObject.getExtensionElements().get("localization"); if (localizationElements != null) { for (ExtensionElement localizationElement : localizationElements) { if (BpmnXMLConstants.ACTIVITI_EXTENSIONS_PREFIX.equals(localizationElement.getNamespacePrefix())) { String locale = localizationElement.getAttributeValue(null, "locale"); String name = localizationElement.getAttributeValue(null, "name"); String documentation = null; List<ExtensionElement> documentationElements = localizationElement.getChildElements().get("documentation"); if (documentationElements != null) { for (ExtensionElement documentationElement : documentationElements) { documentation = StringUtils.trimToNull(documentationElement.getElementText()); break; } } if (name != null && isEqualToCurrentLocalizationValue(locale, dataObject.getName(), DynamicBpmnConstants.LOCALIZATION_NAME, name, infoNode) == false) { dynamicBpmnService.changeLocalizationName(locale, dataObject.getName(), name, infoNode); localizationValuesChanged = true; } if (documentation != null && isEqualToCurrentLocalizationValue(locale, dataObject.getName(), DynamicBpmnConstants.LOCALIZATION_DESCRIPTION, documentation, infoNode) == false) { dynamicBpmnService.changeLocalizationDescription(locale, dataObject.getName(), documentation, infoNode); localizationValuesChanged = true; } } } } } return localizationValuesChanged; } enum ExprType { USER, GROUP } private void addAuthorizationsFromIterator(Set<Expression> exprSet, ProcessDefinitionEntity processDefinition, ExprType exprType) { if (exprSet != null) { Iterator<Expression> iterator = exprSet.iterator(); while (iterator.hasNext()) { Expression expr = (Expression) iterator.next(); IdentityLinkEntity identityLink = new IdentityLinkEntity(); identityLink.setProcessDef(processDefinition); if (exprType.equals(ExprType.USER)) { identityLink.setUserId(expr.toString()); } else if (exprType.equals(ExprType.GROUP)) { identityLink.setGroupId(expr.toString()); } identityLink.setType(IdentityLinkType.CANDIDATE); identityLink.insert(); } } } protected void addAuthorizations(ProcessDefinitionEntity processDefinition) { addAuthorizationsFromIterator(processDefinition.getCandidateStarterUserIdExpressions(), processDefinition, ExprType.USER); addAuthorizationsFromIterator(processDefinition.getCandidateStarterGroupIdExpressions(), processDefinition, ExprType.GROUP); } /** * Returns the default name of the image resource for a certain process. * * It will first look for an image resource which matches the process * specifically, before resorting to an image resource which matches the BPMN * 2.0 xml file resource. * * Example: if the deployment contains a BPMN 2.0 xml resource called * 'abc.bpmn20.xml' containing only one process with key 'myProcess', then * this method will look for an image resources called 'abc.myProcess.png' * (or .jpg, or .gif, etc.) or 'abc.png' if the previous one wasn't found. * * Example 2: if the deployment contains a BPMN 2.0 xml resource called * 'abc.bpmn20.xml' containing three processes (with keys a, b and c), * then this method will first look for an image resource called 'abc.a.png' * before looking for 'abc.png' (likewise for b and c). * Note that if abc.a.png, abc.b.png and abc.c.png don't exist, all * processes will have the same image: abc.png. * * @return null if no matching image resource is found. */ protected String getDiagramResourceForProcess(String bpmnFileResource, String processKey, Map<String, ResourceEntity> resources) { for (String diagramSuffix: DIAGRAM_SUFFIXES) { String diagramForBpmnFileResource = getBpmnFileImageResourceName(bpmnFileResource, diagramSuffix); String processDiagramResource = getProcessImageResourceName(bpmnFileResource, processKey, diagramSuffix); if (resources.containsKey(processDiagramResource)) { return processDiagramResource; } else if (resources.containsKey(diagramForBpmnFileResource)) { return diagramForBpmnFileResource; } } return null; } protected String getBpmnFileImageResourceName(String bpmnFileResource, String diagramSuffix) { String bpmnFileResourceBase = stripBpmnFileSuffix(bpmnFileResource); return bpmnFileResourceBase + diagramSuffix; } protected String getProcessImageResourceName(String bpmnFileResource, String processKey, String diagramSuffix) { String bpmnFileResourceBase = stripBpmnFileSuffix(bpmnFileResource); return bpmnFileResourceBase + processKey + "." + diagramSuffix; } protected String stripBpmnFileSuffix(String bpmnFileResource) { for (String suffix : BPMN_RESOURCE_SUFFIXES) { if (bpmnFileResource.endsWith(suffix)) { return bpmnFileResource.substring(0, bpmnFileResource.length() - suffix.length()); } } return bpmnFileResource; } protected void createResource(String name, byte[] bytes, DeploymentEntity deploymentEntity) { ResourceEntity resource = new ResourceEntity(); resource.setName(name); resource.setBytes(bytes); resource.setDeploymentId(deploymentEntity.getId()); // Mark the resource as 'generated' resource.setGenerated(true); Context .getCommandContext() .getDbSqlSession() .insert(resource); } protected boolean isBpmnResource(String resourceName) { for (String suffix : BPMN_RESOURCE_SUFFIXES) { if (resourceName.endsWith(suffix)) { return true; } } return false; } public ExpressionManager getExpressionManager() { return expressionManager; } public void setExpressionManager(ExpressionManager expressionManager) { this.expressionManager = expressionManager; } public BpmnParser getBpmnParser() { return bpmnParser; } public void setBpmnParser(BpmnParser bpmnParser) { this.bpmnParser = bpmnParser; } public IdGenerator getIdGenerator() { return idGenerator; } public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; } }