/*
* Copyright (c) 2010-2013 Evolveum
*
* 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 com.evolveum.midpoint.wf.impl.activiti;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.impl.WfConfiguration;
import com.evolveum.midpoint.wf.impl.activiti.users.MidPointUserManagerFactory;
import org.activiti.engine.*;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.engine.repository.Deployment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.xml.xpath.XPathExpressionException;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Manages Activiti instance (starts, stops, maintains reference to it).
*
* @author mederly
*/
@Component
public class ActivitiEngine {
private static final Trace LOGGER = TraceManager.getTrace(ActivitiEngine.class);
private ProcessEngine processEngine = null;
@Autowired
private WfConfiguration wfConfiguration;
@PostConstruct
public void init() {
LOGGER.trace("Attempting to create Activiti engine.");
if (!wfConfiguration.isEnabled()) {
LOGGER.trace("Workflows are disabled, exiting.");
return;
}
String schemaUpdate;
if (wfConfiguration.isDropDatabase()) {
schemaUpdate = ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP;
} else {
schemaUpdate = wfConfiguration.isActivitiSchemaUpdate() ?
ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE :
ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE;
}
ProcessEngineConfiguration pec =
new MidPointStandaloneProcessEngineConfiguration() // ugly hack (to provide our own mybatis mapping for Task)
.setDatabaseSchemaUpdate(schemaUpdate);
pec = ((ProcessEngineConfigurationImpl) pec)
// ugly hack - in 5.13 they removed setCustomSessionFactories from ProcessEngineConfiguration abstract class
.setCustomSessionFactories(Collections.singletonList(new MidPointUserManagerFactory()))
.setJobExecutorActivate(false)
.setHistory(HistoryLevel.FULL.getKey());
if (wfConfiguration.getDataSource() != null) {
pec = pec.setDataSourceJndiName(wfConfiguration.getDataSource());
} else {
pec = pec.setJdbcUrl(wfConfiguration.getJdbcUrl())
.setJdbcDriver(wfConfiguration.getJdbcDriver())
.setJdbcUsername(wfConfiguration.getJdbcUser())
.setJdbcPassword(wfConfiguration.getJdbcPassword());
}
pec = pec.setJobExecutorActivate(true);
processEngine = pec.buildProcessEngine();
LOGGER.info("Activiti engine successfully created.");
autoDeploy();
}
private void autoDeploy() {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String[] autoDeploymentFrom = wfConfiguration.getAutoDeploymentFrom();
for (String adf : autoDeploymentFrom) {
Resource[] resources;
try {
resources = resolver.getResources(adf);
} catch (IOException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't get resources to be automatically deployed from " + adf, e);
continue;
}
LOGGER.info("Auto deployment from " + adf + " yields " + resources.length + " resource(s)");
for (Resource resource : resources) {
try {
autoDeployResource(resource);
} catch (IOException | XPathExpressionException | RuntimeException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't deploy the resource " + resource, e);
}
}
}
}
private void autoDeployResource(Resource resource) throws IOException, XPathExpressionException {
RepositoryService repositoryService = processEngine.getRepositoryService();
URL url = resource.getURL();
String name = url.toString();
long resourceLastModified = resource.lastModified();
LOGGER.debug("Checking resource " + name + " (last modified = " + new Date(resourceLastModified) + ")");
boolean tooOld = false;
List<Deployment> existingList = repositoryService.createDeploymentQuery().deploymentName(name).orderByDeploymenTime().desc().listPage(1, 1);
Deployment existing = existingList != null && !existingList.isEmpty() ? existingList.get(0) : null;
if (existing != null) {
if (resourceLastModified >= existing.getDeploymentTime().getTime()) {
tooOld = true;
}
LOGGER.debug("Found deployment " + existing.getName() + ", last modified " + existing.getDeploymentTime() +
(tooOld ? " (too old)" : " (current)"));
} else {
LOGGER.debug("Deployment with name " + name + " was not found.");
}
if (existing == null || tooOld) {
repositoryService.createDeployment().name(name).addInputStream(name, resource.getInputStream()).deploy();
LOGGER.info("Successfully deployed Activiti resource " + name); // + " as deployment with id = " + deployment.getId() + ", name = " + deployment.getName());
}
}
@PreDestroy
public void shutdown() {
if (processEngine != null) {
LOGGER.info("Shutting Activiti ProcessEngine down.");
processEngine.close();
}
}
public ProcessEngine getProcessEngine() {
return processEngine;
}
public HistoryService getHistoryService() {
return processEngine.getHistoryService();
}
public TaskService getTaskService() {
return processEngine.getTaskService();
}
public RuntimeService getRuntimeService() {
return processEngine.getRuntimeService();
}
public IdentityService getIdentityService() {
return processEngine.getIdentityService();
}
public FormService getFormService() {
return processEngine.getFormService();
}
}