package org.activiti.spring.components.config.java.impl;
import org.activiti.engine.*;
import org.activiti.spring.components.aop.ProcessStartAnnotationBeanPostProcessor;
import org.activiti.spring.components.config.StateAnnotationBeanPostProcessor;
import org.activiti.spring.components.config.java.ActivitiConfigurer;
import org.activiti.spring.components.config.java.EnableActiviti;
import org.activiti.spring.components.scope.ProcessScope;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.AbstractLazyCreationTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicReference;
/**
* Much of this code was lifted from the implementation of the {@code @EnableBatchProcessing} annotations
* in the Spring Batch project.
*
* @author Josh Long
*/
@Configuration
@Import(ProcessScopeConfiguration.class)
public class DefaultActvitiConfiguration implements ApplicationContextAware, ImportAware {
private final String enableActivitiClassName = EnableActiviti.class.getName();
private final AtomicReference<ProcessEngine> processEngineAtomicReference = new AtomicReference<ProcessEngine>();
private ApplicationContext applicationContext;
private volatile boolean initialized = false;
private ActivitiConfigurer configurer;
@Bean
public StateAnnotationBeanPostProcessor stateAnnotationBeanPostProcessor(ProcessEngine processEngine) {
return new StateAnnotationBeanPostProcessor(processEngine);
}
private void log(String msg) {
System.out.println(msg);
}
private void log(String msg, Object... parms) {
log(String.format(msg, parms));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
@Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
@Bean
public HistoryService historyService(ProcessEngine processEngine) {
return processEngine.getHistoryService();
}
/**
* creates a proxy that will delegate to a reference that will be provided later on at runtime. This gives the
* references {@code lazy} reference semantics.
*/
protected <T> T createLazyProxy(AtomicReference<T> reference, Class<T> types) {
ProxyFactory factory = new ProxyFactory();
factory.setTargetSource(new ReferenceTargetSource<T>(reference));
factory.addAdvice(new PassthruAdvice());
factory.setInterfaces(new Class[]{types});
return (T) factory.getProxy();
}
protected void initialize() throws Exception {
if (initialized) {
return;
}
if (this.configurer == null)
this.configurer = this.buildActivitiConfigurer(
applicationContext.getBeansOfType(DataSource.class).values(),
applicationContext.getBeansOfType(ActivitiConfigurer.class).values());
processEngineAtomicReference.set(this.configurer.processEngine());
initialized = true;
}
protected ActivitiConfigurer buildActivitiConfigurer(
Collection<DataSource> dataSourceCollection,
Collection<ActivitiConfigurer> configurerCollection) throws Exception {
if (configurerCollection == null || configurerCollection.isEmpty()) {
Assert.isTrue(dataSourceCollection != null && dataSourceCollection.size() == 1, "To use the default " + ActivitiConfigurer.class.getSimpleName() + ", the context must contain precisely one DataSource, found " + (null == dataSourceCollection ? 0 : dataSourceCollection.size()));
DataSource dataSource = dataSourceCollection.iterator().next();
return new DefaultActivitiConfigurer(this.applicationContext, dataSource);
} else {
return configurerCollection.iterator().next();
}
}
@Bean
public ManagementService managementService(ProcessEngine processEngine) {
return processEngine.getManagementService();
}
@Bean
public ProcessEngine processEngine() {
return createLazyProxy(this.processEngineAtomicReference, ProcessEngine.class);
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
AnnotationAttributes enabled = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(enableActivitiClassName, false));
Assert.notNull(enabled, "@" + this.enableActivitiClassName + " is not present on importing class " + importMetadata.getClassName());
}
@Bean
public ProcessStartAnnotationBeanPostProcessor processStartAnnotationBeanPostProcessor(ProcessEngine processEngine) {
ProcessStartAnnotationBeanPostProcessor processStartAnnotationBeanPostProcessor = new ProcessStartAnnotationBeanPostProcessor();
processStartAnnotationBeanPostProcessor.setProcessEngine(processEngine);
return processStartAnnotationBeanPostProcessor;
}
private static class PassthruAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return invocation.proceed();
}
}
private class ReferenceTargetSource<T> extends AbstractLazyCreationTargetSource {
private AtomicReference<T> reference;
public ReferenceTargetSource(AtomicReference<T> reference) {
this.reference = reference;
}
@Override
protected Object createObject() throws Exception {
initialize();
return reference.get();
}
}
}
@Configuration
class ProcessScopeConfiguration {
@Bean
public ProcessScope processScope(ProcessEngine processEngine) {
ProcessScope processScope = new ProcessScope();
processScope.setProcessEngine(processEngine);
return processScope;
}
}