package org.jboss.seam.security.external.dialogues;
import java.lang.annotation.Annotation;
import java.util.UUID;
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.servlet.ServletContext;
import org.jboss.seam.security.external.contexts.ContextualInstance;
import org.jboss.seam.security.external.contexts.HashMapBeanStore;
import org.jboss.seam.security.external.dialogues.api.DialogueScoped;
/**
* @author Marcel Kolsteren
*/
public class DialogueContext implements Context {
private static final String BEAN_STORE_ATTRIBUTE_NAME_PREFIX = "DialogueContextBeanStore";
private ServletContext servletContext;
private final ThreadLocal<String> dialogueIdThreadLocal;
public DialogueContext() {
dialogueIdThreadLocal = new ThreadLocal<String>();
}
protected HashMapBeanStore getBeanStore() {
return getBeanStore(dialogueIdThreadLocal.get());
}
private HashMapBeanStore getBeanStore(String dialogueId) {
HashMapBeanStore beanStore = (HashMapBeanStore) servletContext.getAttribute(getAttributeName(dialogueId));
return beanStore;
}
private void createBeanStore(String dialogueId) {
HashMapBeanStore beanStore = new HashMapBeanStore();
servletContext.setAttribute(getAttributeName(dialogueId), beanStore);
}
private void removeBeanStore(String dialogueId) {
servletContext.removeAttribute(getAttributeName(dialogueId));
}
private String getAttributeName(String dialogueId) {
return BEAN_STORE_ATTRIBUTE_NAME_PREFIX + "_" + dialogueId;
}
public void initialize(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void destroy() {
this.servletContext = null;
}
public String create() {
if (this.dialogueIdThreadLocal.get() != null) {
throw new RuntimeException("Already attached to a dialogue");
}
String dialogueId;
do {
dialogueId = UUID.randomUUID().toString();
}
while (getBeanStore(dialogueId) != null);
this.dialogueIdThreadLocal.set(dialogueId);
createBeanStore(dialogueId);
return dialogueId;
}
public void remove() {
getBeanStore().clear();
removeBeanStore(this.dialogueIdThreadLocal.get());
this.dialogueIdThreadLocal.set(null);
}
public boolean isExistingDialogue(String dialogueId) {
return getBeanStore(dialogueId) != null;
}
/**
* Attaches an existing dialogue to the current thread
*
* @param dialogueIdThreadLocal
*/
public void attach(String dialogueId) {
if (this.dialogueIdThreadLocal.get() != null) {
throw new RuntimeException("Already attached to a dialogue");
}
if (!isExistingDialogue(dialogueId)) {
throw new RuntimeException("There is no active context with request id " + dialogueId);
}
this.dialogueIdThreadLocal.set(dialogueId);
}
/**
* Detaches the dialogue from the current thread
*/
public void detach() {
this.dialogueIdThreadLocal.set(null);
}
public boolean isAttached() {
return dialogueIdThreadLocal.get() != null;
}
public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
if (!isActive()) {
throw new ContextNotActiveException();
}
ContextualInstance<T> beanInstance = getBeanStore().get(contextual);
if (beanInstance != null) {
return beanInstance.getInstance();
} else if (creationalContext != null) {
T instance = contextual.create(creationalContext);
if (instance != null) {
beanInstance = new ContextualInstance<T>(contextual, creationalContext, instance);
getBeanStore().put(contextual, beanInstance);
}
return instance;
} else {
return null;
}
}
public <T> T get(Contextual<T> contextual) {
return get(contextual, null);
}
public Class<? extends Annotation> getScope() {
return DialogueScoped.class;
}
public boolean isActive() {
return dialogueIdThreadLocal.get() != null;
}
}