//$Id: ConversationInterceptor.java 10234 2009-03-30 17:21:15Z dan.j.allen $
package org.jboss.seam.core;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.jboss.seam.annotations.ApplicationException;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.FlushModeType;
import org.jboss.seam.annotations.bpm.BeginTask;
import org.jboss.seam.annotations.bpm.EndTask;
import org.jboss.seam.annotations.bpm.StartTask;
import org.jboss.seam.annotations.intercept.AroundInvoke;
import org.jboss.seam.annotations.intercept.Interceptor;
import org.jboss.seam.bpm.BusinessProcessInterceptor;
import org.jboss.seam.intercept.AbstractInterceptor;
import org.jboss.seam.intercept.InvocationContext;
import org.jboss.seam.navigation.ConversationIdParameter;
import org.jboss.seam.navigation.Pages;
import org.jboss.seam.pageflow.Pageflow;
import org.jboss.seam.persistence.PersistenceContexts;
/**
* Implements annotation-based conversation demarcation.
*
* @author Gavin King
*/
@Interceptor(stateless=true,
around=BijectionInterceptor.class,
within=BusinessProcessInterceptor.class)
public class ConversationInterceptor extends AbstractInterceptor
{
private static final long serialVersionUID = -5405533438107796414L;
@AroundInvoke
public Object aroundInvoke(InvocationContext invocation) throws Exception
{
try
{
Method method = invocation.getMethod();
if ( getComponent().isConversationManagementMethod(method) ) //performance optimization
{
if ( isMissingJoin(method) )
{
throw new IllegalStateException("begin method invoked from a long-running conversation, try using @Begin(join=true) on method: " + method.getName());
}
if ( redirectToExistingConversation(method) )
{
return null;
}
else
{
Object result = invocation.proceed();
beginConversationIfNecessary(method, result);
endConversationIfNecessary(method, result);
return result;
}
}
else
{
return invocation.proceed();
}
}
catch (Exception e)
{
if ( isEndConversationRequired(e) )
{
endConversation(false, false);
}
throw e;
}
}
private boolean isEndConversationRequired(Exception e)
{
Class<? extends Exception> clazz = e.getClass();
return clazz.isAnnotationPresent(ApplicationException.class)
&& clazz.getAnnotation(ApplicationException.class).end();
}
@SuppressWarnings("deprecation")
public boolean redirectToExistingConversation(Method method)
{
if ( !Manager.instance().isLongRunningConversation() )
{
String id = null;
ConversationPropagation propagation = ConversationPropagation.instance();
String conversation = propagation != null ? propagation.getConversationName() : null;
if ( method.isAnnotationPresent(Begin.class) )
{
id = method.getAnnotation(Begin.class).id();
}
else if ( method.isAnnotationPresent(BeginTask.class) )
{
id = method.getAnnotation(BeginTask.class).id();
}
else if ( method.isAnnotationPresent(StartTask.class) )
{
id = method.getAnnotation(StartTask.class).id();
}
if ( id!=null && !"".equals(id) )
{
id = Interpolator.instance().interpolate(id);
ConversationEntry ce = ConversationEntries.instance().getConversationEntry(id);
if (ce==null)
{
Manager.instance().updateCurrentConversationId(id);
}
else
{
return ce.redirect();
}
}
else if (conversation != null && !"".equals(conversation))
{
ConversationIdParameter param = Pages.instance().getConversationIdParameter(conversation);
if (param != null)
{
ConversationEntry ce = ConversationEntries.instance().getConversationEntry(param.getConversationId());
if (ce != null)
{
return ce.redirect();
}
}
}
}
return false;
}
private boolean isMissingJoin(Method method) {
return Manager.instance().isLongRunningOrNestedConversation() && (
(
method.isAnnotationPresent(Begin.class) &&
!method.getAnnotation(Begin.class).join() &&
!method.getAnnotation(Begin.class).nested()
) ||
method.isAnnotationPresent(BeginTask.class) ||
method.isAnnotationPresent(StartTask.class)
);
}
@SuppressWarnings("deprecation")
private void beginConversationIfNecessary(Method method, Object result)
{
boolean simpleBegin =
method.isAnnotationPresent(StartTask.class) ||
method.isAnnotationPresent(BeginTask.class) ||
( method.isAnnotationPresent(Begin.class) && method.getAnnotation(Begin.class).ifOutcome().length==0 );
if ( simpleBegin )
{
if ( result!=null || method.getReturnType().equals(void.class) )
{
boolean nested = false;
if ( method.isAnnotationPresent(Begin.class) )
{
nested = method.getAnnotation(Begin.class).nested();
}
beginConversation( nested, getProcessDefinitionName(method) );
setFlushMode(method); //TODO: what if conversation already exists? Or a nested conversation?
}
}
else if ( method.isAnnotationPresent(Begin.class) )
{
String[] outcomes = method.getAnnotation(Begin.class).ifOutcome();
if ( outcomes.length==0 || Arrays.asList(outcomes).contains(result) )
{
beginConversation(
method.getAnnotation(Begin.class).nested(),
getProcessDefinitionName(method)
);
setFlushMode(method); //TODO: what if conversation already exists? Or a nested conversation?
}
}
}
private void setFlushMode(Method method)
{
FlushModeType flushMode;
if (method.isAnnotationPresent(Begin.class))
{
flushMode = method.getAnnotation(Begin.class).flushMode();
}
else if (method.isAnnotationPresent(BeginTask.class))
{
flushMode = method.getAnnotation(BeginTask.class).flushMode();
}
else if (method.isAnnotationPresent(StartTask.class))
{
flushMode = method.getAnnotation(StartTask.class).flushMode();
}
else
{
return;
}
PersistenceContexts.instance().changeFlushMode(flushMode);
}
private String getProcessDefinitionName(Method method) {
if ( method.isAnnotationPresent(Begin.class) )
{
return method.getAnnotation(Begin.class).pageflow();
}
if ( method.isAnnotationPresent(BeginTask.class) )
{
return method.getAnnotation(BeginTask.class).pageflow();
}
if ( method.isAnnotationPresent(StartTask.class) )
{
return method.getAnnotation(StartTask.class).pageflow();
}
//TODO: let them pass a pageflow name as a request parameter
return "";
}
private void beginConversation(boolean nested, String pageflowName)
{
if ( !Manager.instance().isLongRunningOrNestedConversation() )
{
Manager.instance().beginConversation( );
beginNavigation(pageflowName);
}
else if (nested)
{
Manager.instance().beginNestedConversation();
beginNavigation(pageflowName);
}
}
private void beginNavigation(String pageflowName)
{
if ( !pageflowName.equals("") )
{
if ( !Init.instance().isJbpmInstalled() ) {
throw new IllegalArgumentException("attempting to begin pageflow but required org.jboss.seam.bpm.jbpm component is not installed");
}
Pageflow.instance().begin(pageflowName);
}
}
@SuppressWarnings("deprecation")
private void endConversationIfNecessary(Method method, Object result)
{
boolean isEndAnnotation = method.isAnnotationPresent(End.class);
boolean isEndTaskAnnotation = method.isAnnotationPresent(EndTask.class);
boolean beforeRedirect = ( isEndAnnotation && method.getAnnotation(End.class).beforeRedirect() ) ||
( isEndTaskAnnotation && method.getAnnotation(EndTask.class).beforeRedirect() );
boolean endRoot = ( isEndAnnotation && method.getAnnotation(End.class).root() );
boolean simpleEnd =
( isEndAnnotation && method.getAnnotation(End.class).ifOutcome().length==0 ) ||
( isEndTaskAnnotation && method.getAnnotation(EndTask.class).ifOutcome().length==0 );
if ( simpleEnd )
{
if ( result!=null || method.getReturnType().equals(void.class) ) //null outcome interpreted as redisplay
{
endConversation(beforeRedirect, endRoot);
}
}
else if ( isEndAnnotation )
{
String[] outcomes = method.getAnnotation(End.class).ifOutcome();
if ( Arrays.asList(outcomes).contains(result) )
{
endConversation(beforeRedirect, endRoot);
}
}
else if ( isEndTaskAnnotation )
{
//TODO: fix minor code duplication
String[] outcomes = method.getAnnotation(EndTask.class).ifOutcome();
if ( Arrays.asList(outcomes).contains(result) )
{
endConversation(beforeRedirect, endRoot);
}
}
}
private void endConversation(boolean beforeRedirect, boolean endRoot)
{
if(endRoot)
{
Manager.instance().endRootConversation(beforeRedirect);
}
else
{
Manager.instance().endConversation(beforeRedirect);
}
}
public boolean isInterceptorEnabled()
{
return getComponent().hasConversationManagementMethods();
}
}