/*
* Copyright 2014 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.EntityManagerFactory;
import org.jbpm.kie.services.api.DeploymentIdResolver;
import org.jbpm.kie.services.impl.audit.ServicesAwareAuditEventBuilder;
import org.jbpm.kie.services.impl.security.IdentityRolesSecurityManager;
import org.jbpm.process.audit.event.AuditEventBuilder;
import org.jbpm.runtime.manager.impl.AbstractRuntimeManager;
import org.jbpm.runtime.manager.impl.SimpleRuntimeEnvironment;
import org.jbpm.services.api.DeploymentEvent;
import org.jbpm.services.api.DeploymentEventListener;
import org.jbpm.services.api.DeploymentService;
import org.jbpm.services.api.ListenerSupport;
import org.jbpm.services.api.RuntimeDataService;
import org.jbpm.services.api.model.DeployedUnit;
import org.jbpm.services.api.model.DeploymentUnit;
import org.jbpm.services.api.model.ProcessInstanceDesc;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.manager.RuntimeEnvironment;
import org.kie.api.runtime.manager.RuntimeManager;
import org.kie.api.runtime.manager.RuntimeManagerFactory;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.identity.IdentityProvider;
import org.kie.api.runtime.query.QueryContext;
import org.kie.internal.runtime.conf.DeploymentDescriptor;
import org.kie.internal.runtime.manager.InternalRuntimeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractDeploymentService implements DeploymentService, ListenerSupport {
private static Logger logger = LoggerFactory.getLogger(AbstractDeploymentService.class);
protected RuntimeManagerFactory managerFactory;
protected RuntimeDataService runtimeDataService;
protected EntityManagerFactory emf;
protected IdentityProvider identityProvider;
protected Set<DeploymentEventListener> listeners = new HashSet<DeploymentEventListener>();
@Override
public void addListener(DeploymentEventListener listener) {
this.listeners.add(listener);
}
@Override
public void removeListener(DeploymentEventListener listener) {
this.listeners.remove(listener);
}
@Override
public Collection<DeploymentEventListener> getListeners() {
return Collections.unmodifiableSet(listeners);
}
protected Map<String, DeployedUnit> deploymentsMap = new ConcurrentHashMap<String, DeployedUnit>();
@Override
public void deploy(DeploymentUnit unit) {
if (deploymentsMap.containsKey(unit.getIdentifier())) {
throw new IllegalStateException("Unit with id " + unit.getIdentifier() + " is already deployed");
}
}
public void notifyOnDeploy(DeploymentUnit unit, DeployedUnit deployedUnit){
DeploymentEvent event = new DeploymentEvent(unit.getIdentifier(), deployedUnit);
for (DeploymentEventListener listener : listeners) {
listener.onDeploy(event);
}
}
public void notifyOnUnDeploy(DeploymentUnit unit, DeployedUnit deployedUnit){
DeploymentEvent event = new DeploymentEvent(unit.getIdentifier(), deployedUnit);
for (DeploymentEventListener listener : listeners) {
listener.onUnDeploy(event);
}
}
public void notifyOnActivate(DeploymentUnit unit, DeployedUnit deployedUnit){
DeploymentEvent event = new DeploymentEvent(unit.getIdentifier(), deployedUnit);
for (DeploymentEventListener listener : listeners) {
listener.onActivate(event);
}
}
public void notifyOnDeactivate(DeploymentUnit unit, DeployedUnit deployedUnit){
DeploymentEvent event = new DeploymentEvent(unit.getIdentifier(), deployedUnit);
for (DeploymentEventListener listener : listeners) {
listener.onDeactivate(event);
}
}
public void commonDeploy(DeploymentUnit unit, DeployedUnitImpl deployedUnit, RuntimeEnvironment environemnt, KieContainer kieContainer) {
synchronized (this) {
if (deploymentsMap.containsKey(unit.getIdentifier())) {
DeployedUnit deployed = deploymentsMap.remove(unit.getIdentifier());
RuntimeManager manager = deployed.getRuntimeManager();
manager.close();
}
RuntimeManager manager = null;
deploymentsMap.put(unit.getIdentifier(), deployedUnit);
((SimpleRuntimeEnvironment) environemnt).addToEnvironment("IdentityProvider", identityProvider);
((SimpleRuntimeEnvironment) environemnt).addToEnvironment("Active", deployedUnit.isActive());
try {
switch (unit.getStrategy()) {
case SINGLETON:
manager = managerFactory.newSingletonRuntimeManager(environemnt, unit.getIdentifier());
break;
case PER_REQUEST:
manager = managerFactory.newPerRequestRuntimeManager(environemnt, unit.getIdentifier());
break;
case PER_PROCESS_INSTANCE:
manager = managerFactory.newPerProcessInstanceRuntimeManager(environemnt, unit.getIdentifier());
break;
case PER_CASE:
manager = managerFactory.newPerCaseRuntimeManager(environemnt, unit.getIdentifier());
break;
default:
throw new IllegalArgumentException("Invalid strategy " + unit.getStrategy());
}
((InternalRuntimeManager)manager).setKieContainer(kieContainer);
deployedUnit.setRuntimeManager(manager);
DeploymentDescriptor descriptor = ((InternalRuntimeManager)manager).getDeploymentDescriptor();
List<String> requiredRoles = descriptor.getRequiredRoles(DeploymentDescriptor.TYPE_EXECUTE);
if (requiredRoles != null && !requiredRoles.isEmpty()) {
((InternalRuntimeManager)manager).setSecurityManager(new IdentityRolesSecurityManager(identityProvider, requiredRoles));
}
notifyOnDeploy(unit, deployedUnit);
} catch (Throwable e) {
deploymentsMap.remove(unit.getIdentifier());
if (manager != null) {
manager.close();
}
notifyOnUnDeploy(unit, deployedUnit);
throw new RuntimeException(e);
}
}
}
@Override
public void undeploy(DeploymentUnit unit) {
List<Integer> states = new ArrayList<Integer>();
states.add(ProcessInstance.STATE_ACTIVE);
states.add(ProcessInstance.STATE_PENDING);
states.add(ProcessInstance.STATE_SUSPENDED);
Collection<ProcessInstanceDesc> activeProcesses = runtimeDataService.getProcessInstancesByDeploymentId(unit.getIdentifier(), states, new QueryContext());
if (!activeProcesses.isEmpty()) {
throw new IllegalStateException("Undeploy forbidden - there are active processes instances for deployment "
+ unit.getIdentifier());
}
synchronized (this) {
DeployedUnit deployed = deploymentsMap.remove(unit.getIdentifier());
if (deployed != null) {
RuntimeManager manager = deployed.getRuntimeManager();
((AbstractRuntimeManager)manager).close(true);
}
notifyOnUnDeploy(unit, deployed);
}
}
@Override
public RuntimeManager getRuntimeManager(String deploymentUnitId) {
if (deploymentUnitId != null && deploymentsMap.containsKey(deploymentUnitId)) {
return deploymentsMap.get(deploymentUnitId).getRuntimeManager();
} else if (deploymentUnitId != null && deploymentUnitId.toLowerCase().contains("latest")) {
String matched = DeploymentIdResolver.matchAndReturnLatest(deploymentUnitId, deploymentsMap.keySet());
return deploymentsMap.get(matched).getRuntimeManager();
}
return null;
}
@Override
public DeployedUnit getDeployedUnit(String deploymentUnitId) {
DeployedUnit deployedUnit = null;
if (deploymentsMap.containsKey(deploymentUnitId)) {
deployedUnit = deploymentsMap.get(deploymentUnitId);
} else if (deploymentUnitId != null && deploymentUnitId.toLowerCase().contains("latest")) {
String matched = DeploymentIdResolver.matchAndReturnLatest(deploymentUnitId, deploymentsMap.keySet());
deployedUnit = deploymentsMap.get(matched);
}
return deployedUnit;
}
public Map<String, DeployedUnit> getDeploymentsMap() {
return deploymentsMap;
}
@Override
public Collection<DeployedUnit> getDeployedUnits() {
return Collections.unmodifiableCollection(deploymentsMap.values()) ;
}
public RuntimeManagerFactory getManagerFactory() {
return managerFactory;
}
public void setManagerFactory(RuntimeManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
public RuntimeDataService getRuntimeDataService() {
return runtimeDataService;
}
public EntityManagerFactory getEmf() {
return emf;
}
public void setEmf(EntityManagerFactory emf) {
this.emf = emf;
}
public void setRuntimeDataService(RuntimeDataService runtimeDataService) {
this.runtimeDataService = runtimeDataService;
}
public void setIdentityProvider(IdentityProvider identityProvider) {
this.identityProvider = identityProvider;
}
protected AuditEventBuilder setupAuditLogger(IdentityProvider identityProvider, String deploymentUnitId) {
ServicesAwareAuditEventBuilder auditEventBuilder = new ServicesAwareAuditEventBuilder();
auditEventBuilder.setIdentityProvider(identityProvider);
auditEventBuilder.setDeploymentUnitId(deploymentUnitId);
return auditEventBuilder;
}
@Override
public boolean isDeployed(String deploymentUnitId) {
return deploymentsMap.containsKey(deploymentUnitId);
}
public void shutdown() {
Collection<DeployedUnit> deployedUnits = getDeployedUnits();
for (DeployedUnit deployed : deployedUnits) {
try {
deployed.getRuntimeManager().close();
} catch (Exception e) {
logger.warn("Error encountered while shutting down deplyment {} due to {}",
deployed.getDeploymentUnit().getIdentifier(), e.getMessage());
}
}
}
}