package com.enioka.jqm.handler;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.Scope;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.SimpleThreadScope;
import com.enioka.jqm.api.JobInstanceStartedHandler;
import com.enioka.jqm.api.JobManager;
/**
* A handler which creates a Spring annotation parsing context if it does not already exists. Can take optional parameters which specify
* additional packages to scan. <br>
* <br>
* As for all event handlers, it should not be instantiated directly but be referenced in a <context> tag in a deployment descriptor.
*/
public class AnnotationSpringContextBootstrapHandler implements JobInstanceStartedHandler
{
private static AnnotationConfigApplicationContext ctx;
private static volatile Boolean refreshed = false;
private final static String THREAD_SCOPE_NAME = "thread";
static
{
// Implementation choice: we use annotations.
ctx = new AnnotationConfigApplicationContext();
// There is no "request" scope in an AnnotationConfigApplicationContext, as we are not inside a web container.
// For JQM, we register a "thread" scope as one job instance = one thread, so it can be most useful.
Scope threadScope = new SimpleThreadScope();
ctx.getBeanFactory().registerScope(THREAD_SCOPE_NAME, threadScope);
}
public static Object getBean(String beanName)
{
return ctx.getBean(beanName);
}
public static Object getBean(Class<? extends Object> clazz)
{
return ctx.getBean(clazz);
}
@SuppressWarnings("unchecked")
@Override
public void run(Class<? extends Object> toRun, JobManager handler, Map<String, String> handlerParameters)
{
// initialize the spring context with all requested classes on first boot -
// and only on first boot: the Spring context cannot be refreshed multiple times.
if (!refreshed)
{
synchronized (refreshed)
{
if (!refreshed)
{
// Add the different elements to the context path
if (handlerParameters.containsKey("beanNameGenerator"))
{
Class<? extends BeanNameGenerator> clazz;
try
{
clazz = (Class<? extends BeanNameGenerator>) AnnotationSpringContextBootstrapHandler.class.getClassLoader()
.loadClass(handlerParameters.get("beanNameGenerator"));
}
catch (ClassNotFoundException e)
{
throw new RuntimeException("The handler beanNameGenerator class [" + handlerParameters.get("beanNameGenerator")
+ "] cannot be found.", e);
}
BeanNameGenerator generator;
try
{
generator = clazz.newInstance();
}
catch (Exception e)
{
throw new RuntimeException("The handler beanNameGenerator class [" + handlerParameters.get("beanNameGenerator")
+ "] was found but cannot be instantiated. Check it has a no-arguments constructor.", e);
}
ctx.setBeanNameGenerator(generator);
}
if (handlerParameters.containsKey("contextDisplayName"))
{
ctx.setDisplayName(handlerParameters.get("contextDisplayName"));
}
if (handlerParameters.containsKey("contextId"))
{
ctx.setId(handlerParameters.get("contextId"));
}
if (handlerParameters.containsKey("allowCircularReferences"))
{
boolean allow = Boolean.parseBoolean(handlerParameters.get("allowCircularReferences"));
ctx.setAllowCircularReferences(allow);
}
if (handlerParameters.containsKey("additionalScan"))
{
ctx.scan(handlerParameters.get("additionalScan").split(","));
}
else
{
ctx.register(toRun);
}
// Create a holder for JQM injected items.
BeanDefinition def = new RootBeanDefinition(HashMap.class);
def.setScope(THREAD_SCOPE_NAME);
ctx.registerBeanDefinition("runtimeParameters", def);
def = new RootBeanDefinition(JobManager.class);
def.setScope(THREAD_SCOPE_NAME);
ctx.registerBeanDefinition("jobManager", def);
// Go: this initializes the context
ctx.refresh();
refreshed = true;
}
}
}
}
}