/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* 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.jbpm.kie.services.impl.bpmn2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl;
import org.drools.core.io.impl.ByteArrayResource;
import org.drools.core.util.StringUtils;
import org.jbpm.kie.services.impl.model.ProcessAssetDesc;
import org.jbpm.process.core.impl.ProcessImpl;
import org.jbpm.services.api.DefinitionService;
import org.jbpm.services.api.DeploymentEvent;
import org.jbpm.services.api.DeploymentEventListener;
import org.jbpm.services.api.model.ProcessDefinition;
import org.jbpm.services.api.model.UserTaskDefinition;
import org.kie.api.definition.process.Process;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderError;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.definition.KnowledgePackage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BPMN2DataServiceImpl implements DefinitionService, DeploymentEventListener {
private static final Logger logger = LoggerFactory.getLogger(BPMN2DataServiceImpl.class);
private ConcurrentMap<String, Map<String, ProcessDescriptor>> definitionCache =
new ConcurrentHashMap<String, Map<String, ProcessDescriptor>>();
public BPMN2DataServiceImpl() {
}
private void validateNonEmptyDeploymentIdAndProcessId(String deploymentId, String processId) {
validateNonEmptyDeploymentIdAndProcessIdAndTaskName(deploymentId, processId, "x");
}
private void validateNonEmptyDeploymentIdAndProcessIdAndTaskName(String deploymentId, String processId, String taskName) {
boolean emptyDepId = StringUtils.isEmpty(deploymentId);
boolean emptyProcId = StringUtils.isEmpty(processId);
boolean emptyTaskName = StringUtils.isEmpty(taskName);
if ( emptyDepId || emptyProcId || emptyTaskName ) {
StringBuffer msg = new StringBuffer("The ");
if( emptyDepId ) {
msg.append( "deployment id " );
}
if( emptyDepId && ( emptyProcId || emptyTaskName ) ) {
msg.append( "and the " );
}
if( emptyProcId ) {
msg.append( "process id " );
}
if( emptyDepId && emptyTaskName ) {
msg.append( "and the " );
}
if( emptyTaskName ) {
msg.append( "task name " );
}
msg.append( "may not be empty or null!");
throw new IllegalStateException( msg.toString() );
}
}
public void addProcessDefinition(String deploymentId, String processId, Object processDescriptor, KieContainer kieContainer) {
Map<String, ProcessDescriptor> definitions = null;
synchronized (definitionCache) {
Map<String, ProcessDescriptor> newDef = new ConcurrentHashMap<String, ProcessDescriptor>();
definitions = definitionCache.putIfAbsent(deploymentId, newDef);
if( definitions == null ) {
definitions = newDef;
}
ProcessDescriptor descriptor = (ProcessDescriptor) processDescriptor;
fillProcessDefinition(descriptor, kieContainer);
definitions.put(processId, descriptor);
}
}
@Override
public ProcessDefinition buildProcessDefinition(String deploymentId, String bpmn2Content, KieContainer kieContainer, boolean cache)
throws IllegalArgumentException {
if (StringUtils.isEmpty(bpmn2Content)) {
return null;
}
KnowledgeBuilder kbuilder = null;
if (kieContainer != null && kieContainer.getClassLoader() != null ) {
KnowledgeBuilderConfigurationImpl pconf = new KnowledgeBuilderConfigurationImpl(kieContainer.getClassLoader());
kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(pconf);
} else {
kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
}
kbuilder.add(new ByteArrayResource(bpmn2Content.getBytes()), ResourceType.BPMN2);
if (kbuilder.hasErrors()) {
for(KnowledgeBuilderError error: kbuilder.getErrors()){
logger.error("Error: {}", error.getMessage());
}
logger.debug("Process Cannot be Parsed! \n {} \n", bpmn2Content);
return null;
}
KnowledgePackage pckg = kbuilder.getKnowledgePackages().iterator().next();
Process process = pckg.getProcesses().iterator().next();
ProcessDescriptor helper = (ProcessDescriptor) process.getMetaData().get("ProcessDescriptor");
ProcessAssetDesc definition = fillProcessDefinition(helper, kieContainer);
// cache the data if requested
if (cache) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, "no proc id");
Map<String, ProcessDescriptor> definitions = null;
synchronized (definitionCache) {
Map<String, ProcessDescriptor> newDef = new ConcurrentHashMap<String, ProcessDescriptor>();
definitions = definitionCache.putIfAbsent(deploymentId, newDef);
if( definitions == null ) {
definitions = newDef;
}
definitions.put(process.getId(), helper);
}
}
return definition;
}
private ProcessAssetDesc fillProcessDefinition(ProcessDescriptor helper, KieContainer kieContainer ) {
ProcessAssetDesc definition = helper.getProcess();
definition.setAssociatedEntities(helper.getTaskAssignments());
definition.setProcessVariables(helper.getInputs());
definition.setServiceTasks(helper.getServiceTasks());
definition.setSignals(helper.getSignals() );
definition.setGlobals(helper.getGlobals() );
definition.setReferencedRules(helper.getReferencedRules() );
if( kieContainer != null && helper.hasUnresolvedReusableSubProcessNames() ) {
helper.resolveReusableSubProcessNames(kieContainer.getKieBase().getProcesses());
}
definition.setReusableSubProcesses(helper.getReusableSubProcesses());
return definition;
}
@Override
public Map<String, String> getServiceTasks(String deploymentId, String processId) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
return Collections.unmodifiableMap(helper.getServiceTasks());
}
return Collections.emptyMap();
}
@Override
public ProcessDefinition getProcessDefinition(String deploymentId, String processId) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
return helper.getProcess();
}
return null;
}
@Override
public Collection<String> getReusableSubProcesses(String deploymentId, String processId) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
if (helper.getReusableSubProcesses() != null) {
return new ArrayList<String>(helper.getReusableSubProcesses());
}
}
return Collections.emptyList();
}
@Override
public Map<String, String> getProcessVariables(String deploymentId, String processId) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
if (helper.getInputs() != null) {
return Collections.unmodifiableMap(helper.getInputs());
}
}
return Collections.emptyMap();
}
@Override
public Map<String, Collection<String>> getAssociatedEntities(String deploymentId, String processId) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
if (helper.getTaskAssignments() != null) {
return Collections.unmodifiableMap(helper.getTaskAssignments());
}
}
return Collections.emptyMap();
}
@Override
public Collection<UserTaskDefinition> getTasksDefinitions(String deploymentId, String processId) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
if (helper.getTasks() != null) {
return new ArrayList<UserTaskDefinition>(helper.getTasks().values());
}
}
return Collections.emptyList();
}
@Override
public Map<String, String> getTaskInputMappings(String deploymentId,String processId, String taskName) {
validateNonEmptyDeploymentIdAndProcessIdAndTaskName(deploymentId, processId, taskName);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
if (helper.getTaskInputMappings().containsKey(taskName)) {
return Collections.unmodifiableMap(helper.getTaskInputMappings().get(taskName));
}
}
return Collections.emptyMap();
}
@Override
public Map<String, String> getTaskOutputMappings(String deploymentId, String processId, String taskName) {
validateNonEmptyDeploymentIdAndProcessIdAndTaskName(deploymentId, processId, taskName);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
if (helper.getTaskOutputMappings().containsKey(taskName)) {
return Collections.unmodifiableMap(helper.getTaskOutputMappings().get(taskName));
}
}
return Collections.emptyMap();
}
@Override
public void onDeploy(DeploymentEvent event) {
// no op
}
@Override
public void onUnDeploy(DeploymentEvent event) {
// remove process definitions from the cache
definitionCache.remove(event.getDeploymentId());
}
@Override
public void onActivate(DeploymentEvent event) {
// no op
}
@Override
public void onDeactivate(DeploymentEvent event) {
// no op
}
@Override
public Set<String> getJavaClasses( String deploymentId, String processId ) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
return Collections.unmodifiableSet(helper.getReferencedClasses());
}
return Collections.emptySet();
}
@Override
public Set<String> getRuleSets( String deploymentId, String processId ) {
validateNonEmptyDeploymentIdAndProcessId(deploymentId, processId);
if (definitionCache.containsKey(deploymentId)) {
ProcessDescriptor helper = definitionCache.get(deploymentId).get(processId);
if (helper == null) {
throw new IllegalStateException("No process available with given id : " + processId);
}
return Collections.unmodifiableSet(helper.getReferencedRules());
}
return Collections.emptySet();
}
}