All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.camunda.bpm.engine.impl.AbstractDefinitionDeployer Maven / Gradle / Ivy

There is a newer version: 7.22.0-alpha1
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; 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.camunda.bpm.engine.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.impl.cfg.IdGenerator;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.cmd.CommandLogger;
import org.camunda.bpm.engine.impl.context.Context;
import org.camunda.bpm.engine.impl.core.model.Properties;
import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.persistence.deploy.Deployer;
import org.camunda.bpm.engine.impl.persistence.deploy.cache.DeploymentCache;
import org.camunda.bpm.engine.impl.persistence.entity.DeploymentEntity;
import org.camunda.bpm.engine.impl.persistence.entity.ResourceEntity;
import org.camunda.bpm.engine.impl.repository.ResourceDefinitionEntity;

/**
 * {@link Deployer} responsible to parse resource files and create the proper entities.
 * This class is extended by specific resource deployers.
 *
 * Note: Implementations must be thread-safe. In particular they should not keep deployment-specific state.
 */
public abstract class AbstractDefinitionDeployer implements Deployer {

  public static final String[] DIAGRAM_SUFFIXES = new String[] { "png", "jpg", "gif", "svg" };

  private final CommandLogger LOG = ProcessEngineLogger.CMD_LOGGER;

  protected IdGenerator idGenerator;

  public IdGenerator getIdGenerator() {
    return idGenerator;
  }

  public void setIdGenerator(IdGenerator idGenerator) {
    this.idGenerator = idGenerator;
  }

  public void deploy(DeploymentEntity deployment) {
    LOG.debugProcessingDeployment(deployment.getName());
    Properties properties = new Properties();
    List definitions = parseDefinitionResources(deployment, properties);
    ensureNoDuplicateDefinitionKeys(definitions);
    postProcessDefinitions(deployment, definitions, properties);
  }

  protected List parseDefinitionResources(DeploymentEntity deployment, Properties properties) {
    List definitions = new ArrayList();
    for (ResourceEntity resource : deployment.getResources().values()) {
      LOG.debugProcessingResource(resource.getName());
      if (isResourceHandled(resource)) {
        definitions.addAll(transformResource(deployment, resource, properties));
      }
    }
    return definitions;
  }

  protected boolean isResourceHandled(ResourceEntity resource) {
    String resourceName = resource.getName();

    for (String suffix : getResourcesSuffixes()) {
      if (resourceName.endsWith(suffix)) {
        return true;
      }
    }

    return false;
  }

  /**
   * @return the list of resource suffixes for this cacheDeployer
   */
  protected abstract String[] getResourcesSuffixes();

  protected Collection transformResource(DeploymentEntity deployment, ResourceEntity resource, Properties properties) {
    String resourceName = resource.getName();
    List definitions = transformDefinitions(deployment, resource, properties);

    for (DefinitionEntity definition : definitions) {
      definition.setResourceName(resourceName);

      String diagramResourceName = getDiagramResourceForDefinition(deployment, resourceName, definition, deployment.getResources());
      if (diagramResourceName != null) {
        definition.setDiagramResourceName(diagramResourceName);
      }
    }

    return definitions;
  }


  /**
   * Transform the resource entity into definition entities.
   *
   * @param deployment the deployment the resources belongs to
   * @param resource the resource to transform
   * @return a list of transformed definition entities
   */
  protected abstract List transformDefinitions(DeploymentEntity deployment, ResourceEntity resource, Properties properties);

  /**
   * Returns the default name of the image resource for a certain definition.
   *
   * It will first look for an image resource which matches the definition
   * specifically, before resorting to an image resource which matches the file
   * containing the definition.
   *
   * 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 getDiagramResourceForDefinition(DeploymentEntity deployment, String resourceName, DefinitionEntity definition, Map resources) {
    for (String diagramSuffix: getDiagramSuffixes()) {
      String definitionDiagramResource = getDefinitionDiagramResourceName(resourceName, definition, diagramSuffix);
      String diagramForFileResource = getGeneralDiagramResourceName(resourceName, definition, diagramSuffix);
      if (resources.containsKey(definitionDiagramResource)) {
        return definitionDiagramResource;
      } else if (resources.containsKey(diagramForFileResource)) {
        return diagramForFileResource;
      }
    }
    // no matching diagram found
    return null;
  }

  protected String getDefinitionDiagramResourceName(String resourceName, DefinitionEntity definition, String diagramSuffix) {
    String fileResourceBase = stripDefinitionFileSuffix(resourceName);
    String definitionKey = definition.getKey();

    return fileResourceBase + definitionKey + "." + diagramSuffix;
  }

  protected String getGeneralDiagramResourceName(String resourceName, DefinitionEntity definition, String diagramSuffix) {
    String fileResourceBase = stripDefinitionFileSuffix(resourceName);

    return fileResourceBase + diagramSuffix;
  }

  protected String stripDefinitionFileSuffix(String resourceName) {
    for (String suffix : getResourcesSuffixes()) {
      if(resourceName.endsWith(suffix)) {
        return resourceName.substring(0, resourceName.length() - suffix.length());
      }
    }
    return resourceName;
  }

  protected String[] getDiagramSuffixes() {
    return DIAGRAM_SUFFIXES;
  }

  protected void ensureNoDuplicateDefinitionKeys(List definitions) {
    Set keys = new HashSet();

    for (DefinitionEntity definition : definitions) {

      String key = definition.getKey();

      if (keys.contains(key)) {
        throw new ProcessEngineException("The deployment contains definitions with the same key '" + key + "' (id attribute), this is not allowed");
      }

      keys.add(key);
    }
  }

  protected void postProcessDefinitions(DeploymentEntity deployment, List definitions, Properties properties) {
    if (deployment.isNew()) {
      // if the deployment is new persist the new definitions
      persistDefinitions(deployment, definitions, properties);
    } else {
      // if the current deployment is not a new one,
      // then load the already existing definitions
      loadDefinitions(deployment, definitions, properties);
    }
  }

  protected void persistDefinitions(DeploymentEntity deployment, List definitions, Properties properties) {
    for (DefinitionEntity definition : definitions) {
      String definitionKey = definition.getKey();
      String tenantId = deployment.getTenantId();

      DefinitionEntity latestDefinition = findLatestDefinitionByKeyAndTenantId(definitionKey, tenantId);

      updateDefinitionByLatestDefinition(deployment, definition, latestDefinition);

      persistDefinition(definition);
      registerDefinition(deployment, definition, properties);
    }
  }

  protected void updateDefinitionByLatestDefinition(DeploymentEntity deployment, DefinitionEntity definition, DefinitionEntity latestDefinition) {
    definition.setVersion(getNextVersion(deployment, definition, latestDefinition));
    definition.setId(generateDefinitionId(deployment, definition, latestDefinition));
    definition.setDeploymentId(deployment.getId());
    definition.setTenantId(deployment.getTenantId());
  }

  protected void loadDefinitions(DeploymentEntity deployment, List definitions, Properties properties) {
    for (DefinitionEntity definition : definitions) {
      String deploymentId = deployment.getId();
      String definitionKey = definition.getKey();

      DefinitionEntity persistedDefinition = findDefinitionByDeploymentAndKey(deploymentId, definitionKey);
      handlePersistedDefinition(definition, persistedDefinition, deployment, properties);
    }
  }

  protected void handlePersistedDefinition(DefinitionEntity definition,
            DefinitionEntity persistedDefinition, DeploymentEntity deployment, Properties properties) {
    persistedDefinitionLoaded(deployment, definition, persistedDefinition);
    updateDefinitionByPersistedDefinition(deployment, definition, persistedDefinition);
    registerDefinition(deployment, definition, properties);
  }

  protected void updateDefinitionByPersistedDefinition(DeploymentEntity deployment, DefinitionEntity definition, DefinitionEntity persistedDefinition) {
    definition.setVersion(persistedDefinition.getVersion());
    definition.setId(persistedDefinition.getId());
    definition.setDeploymentId(deployment.getId());
    definition.setTenantId(persistedDefinition.getTenantId());
  }

  /**
   * Called when a previous version of a definition was loaded from the persistent store.
   *
   * @param deployment the deployment of the definition
   * @param definition the definition entity
   * @param persistedDefinition the loaded definition entity
   */
  protected void persistedDefinitionLoaded(DeploymentEntity deployment, DefinitionEntity definition, DefinitionEntity persistedDefinition) {
    // do nothing;
  }

  /**
   * Find a definition entity by deployment id and definition key.
   * @param deploymentId the deployment id
   * @param definitionKey the definition key
   * @return the corresponding definition entity or null if non is found
   */
  protected abstract DefinitionEntity findDefinitionByDeploymentAndKey(String deploymentId, String definitionKey);

  /**
   * Find the last deployed definition entity by definition key and tenant id.
   *
   * @return the corresponding definition entity or null if non is found
   */
  protected abstract DefinitionEntity findLatestDefinitionByKeyAndTenantId(String definitionKey, String tenantId);

  /**
   * Persist definition entity into the database.
   * @param definition the definition entity
   */
  protected abstract void persistDefinition(DefinitionEntity definition);

  protected void registerDefinition(DeploymentEntity deployment, DefinitionEntity definition, Properties properties) {
    DeploymentCache deploymentCache = getDeploymentCache();

    // Add to cache
    addDefinitionToDeploymentCache(deploymentCache, definition);

    definitionAddedToDeploymentCache(deployment, definition, properties);

    // Add to deployment for further usage
    deployment.addDeployedArtifact(definition);
  }

  /**
   * Add a definition to the deployment cache
   *
   * @param deploymentCache the deployment cache
   * @param definition the definition to add
   */
  protected abstract void addDefinitionToDeploymentCache(DeploymentCache deploymentCache, DefinitionEntity definition);

  /**
   * Called after a definition was added to the deployment cache.
   *
   * @param deployment the deployment of the definition
   * @param definition the definition entity
   */
  protected void definitionAddedToDeploymentCache(DeploymentEntity deployment, DefinitionEntity definition, Properties properties) {
    // do nothing
  }

  /**
   * per default we increment the latest definition version by one - but you
   * might want to hook in some own logic here, e.g. to align definition
   * versions with deployment / build versions.
   */
  protected int getNextVersion(DeploymentEntity deployment, DefinitionEntity newDefinition, DefinitionEntity latestDefinition) {
    int result = 1;
    if (latestDefinition != null) {
      int latestVersion = latestDefinition.getVersion();
      result = latestVersion + 1;
    }
    return result;
  }

  /**
   * create an id for the definition. The default is to ask the {@link IdGenerator}
   * and add the definition key and version if that does not exceed 64 characters.
   * You might want to hook in your own implementation here.
   */
  protected String generateDefinitionId(DeploymentEntity deployment, DefinitionEntity newDefinition, DefinitionEntity latestDefinition) {
    String nextId = idGenerator.getNextId();

    String definitionKey = newDefinition.getKey();
    int definitionVersion = newDefinition.getVersion();

    String definitionId = definitionKey
      + ":" + definitionVersion
      + ":" + nextId;

    // ACT-115: maximum id length is 64 characters
    if (definitionId.length() > 64) {
      definitionId = nextId;
    }
    return definitionId;
  }

  protected ProcessEngineConfigurationImpl getProcessEngineConfiguration() {
    return Context.getProcessEngineConfiguration();
  }

  protected CommandContext getCommandContext() {
    return Context.getCommandContext();
  }

  protected DeploymentCache getDeploymentCache() {
    return getProcessEngineConfiguration().getDeploymentCache();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy