package org.jboss.seam.wicket.ioc; import java.lang.reflect.Method; import java.util.Arrays; import org.apache.wicket.AbortException; import org.apache.wicket.Page; import org.apache.wicket.RequestCycle; import org.apache.wicket.Session; import org.jboss.seam.NoConversationException; import org.jboss.seam.annotations.ApplicationException; import org.jboss.seam.annotations.Begin; import org.jboss.seam.annotations.Conversational; 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.core.ConversationEntries; import org.jboss.seam.core.ConversationEntry; import org.jboss.seam.core.ConversationPropagation; import org.jboss.seam.core.Interpolator; import org.jboss.seam.core.Manager; import org.jboss.seam.international.StatusMessage; import org.jboss.seam.international.StatusMessages; import org.jboss.seam.navigation.ConversationIdParameter; import org.jboss.seam.navigation.Pages; import org.jboss.seam.pageflow.Pageflow; import org.jboss.seam.persistence.PersistenceContexts; public class ConversationInterceptor<T> implements StatelessInterceptor<T> { public void beforeInvoke(InvocationContext<T> invocationContext) { if (invocationContext.getComponent().isConversationManagementMethod(invocationContext.getAccessibleObject())) { if ( isMissingJoin(invocationContext) ) { throw new IllegalStateException("begin method invoked from a long-running conversation, try using @Begin(join=true) on method: " + invocationContext.getMember().getName()); } checkForConversation(invocationContext); } } public Object afterInvoke(InvocationContext<T> invocationContext, Object result) { if (invocationContext.getComponent().isConversationManagementMethod(invocationContext.getAccessibleObject())) { beginConversationIfNecessary(invocationContext, result); endConversationIfNecessary(invocationContext, result); } return result; } public Exception handleException(InvocationContext<T> invocationContext, Exception exception) { if ( isEndConversationRequired(exception) ) { endConversation(false); } return exception; } 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(InvocationContext invocationContext) { return Manager.instance().isLongRunningOrNestedConversation() && ( ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) && !invocationContext.getAccessibleObject().getAnnotation(Begin.class).join() && !invocationContext.getAccessibleObject().getAnnotation(Begin.class).nested() ) || ( invocationContext.getAccessibleObject().isAnnotationPresent(org.jboss.seam.wicket.annotations.Begin.class) && !invocationContext.getAccessibleObject().getAnnotation(org.jboss.seam.wicket.annotations.Begin.class).join() && !invocationContext.getAccessibleObject().getAnnotation(org.jboss.seam.wicket.annotations.Begin.class).nested() ) || invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) || invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class) ); } private void checkForConversation(InvocationContext<T> invocationContext) { if (!Manager.instance().isLongRunningConversation() && invocationContext.getAccessibleObject().isAnnotationPresent(Conversational.class)) { Class<? extends Page> noConversationPage = invocationContext.getComponent().getNoConversationPage(); if (noConversationPage != null) { final RequestCycle cycle = RequestCycle.get(); StatusMessages.instance().addFromResourceBundleOrDefault( StatusMessage.Severity.WARN, "org.jboss.seam.NoConversation", "The conversation ended or timed" ); cycle.redirectTo(Session.get().getPageFactory().newPage(noConversationPage)); throw new AbortException(); } else { throw new NoConversationException( "no long-running conversation for @Conversational wicket component: " + invocationContext.getComponent().getClass().getName()); } } } @SuppressWarnings("deprecation") private void beginConversationIfNecessary(InvocationContext invocationContext, Object result) { boolean simpleBegin = invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class) || invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) || invocationContext.getAccessibleObject().isAnnotationPresent(org.jboss.seam.wicket.annotations.Begin.class) || ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) && invocationContext.getAccessibleObject().getAnnotation(Begin.class).ifOutcome().length==0 ); if ( simpleBegin ) { if ( result!=null || ( invocationContext.getMethod() != null && invocationContext.getMethod().getReturnType().equals(void.class)) || invocationContext.getConstructor() != null ) { boolean nested = false; if ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) ) { nested = invocationContext.getAccessibleObject().getAnnotation(Begin.class).nested(); } else if ( invocationContext.getAccessibleObject().isAnnotationPresent(org.jboss.seam.wicket.annotations.Begin.class) ) { nested = invocationContext.getAccessibleObject().getAnnotation(org.jboss.seam.wicket.annotations.Begin.class).nested(); } beginConversation( nested, getProcessDefinitionName(invocationContext) ); setFlushMode(invocationContext); //TODO: what if conversation already exists? Or a nested conversation? } } else if ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) ) { String[] outcomes = invocationContext.getAccessibleObject().getAnnotation(Begin.class).ifOutcome(); if ( outcomes.length==0 || Arrays.asList(outcomes).contains(result) ) { beginConversation( invocationContext.getAccessibleObject().getAnnotation(Begin.class).nested(), getProcessDefinitionName(invocationContext) ); setFlushMode(invocationContext); //TODO: what if conversation already exists? Or a nested conversation? } } } private void setFlushMode(InvocationContext<T> invocationContext) { FlushModeType flushMode; if (invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class)) { flushMode = invocationContext.getAccessibleObject().getAnnotation(Begin.class).flushMode(); } else if (invocationContext.getAccessibleObject().isAnnotationPresent(org.jboss.seam.wicket.annotations.Begin.class)) { flushMode = invocationContext.getAccessibleObject().getAnnotation(org.jboss.seam.wicket.annotations.Begin.class).flushMode(); } else if (invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class)) { flushMode = invocationContext.getAccessibleObject().getAnnotation(BeginTask.class).flushMode(); } else if (invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class)) { flushMode = invocationContext.getAccessibleObject().getAnnotation(StartTask.class).flushMode(); } else { return; } PersistenceContexts.instance().changeFlushMode(flushMode); } private String getProcessDefinitionName(InvocationContext invocationContext) { if ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) ) { return invocationContext.getAccessibleObject().getAnnotation(Begin.class).pageflow(); } if ( invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) ) { return invocationContext.getAccessibleObject().getAnnotation(BeginTask.class).pageflow(); } if ( invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class) ) { return invocationContext.getAccessibleObject().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("") ) { Pageflow.instance().begin(pageflowName); } } @SuppressWarnings("deprecation") private void endConversationIfNecessary(InvocationContext<T> invocationContext, Object result) { boolean isEndAnnotation = invocationContext.getAccessibleObject().isAnnotationPresent(End.class); boolean isEndTaskAnnotation = invocationContext.getAccessibleObject().isAnnotationPresent(EndTask.class); boolean beforeRedirect = ( isEndAnnotation && invocationContext.getAccessibleObject().getAnnotation(End.class).beforeRedirect() ) || ( isEndTaskAnnotation && invocationContext.getAccessibleObject().getAnnotation(EndTask.class).beforeRedirect() ); boolean simpleEnd = ( isEndAnnotation && invocationContext.getAccessibleObject().getAnnotation(End.class).ifOutcome().length==0 ) || ( isEndTaskAnnotation && invocationContext.getAccessibleObject().getAnnotation(EndTask.class).ifOutcome().length==0 ); if ( simpleEnd ) { if ( result!=null || invocationContext.getConstructor() != null || (invocationContext.getMethod() != null && invocationContext.getMethod().getReturnType().equals(void.class)) ) //null outcome interpreted as redisplay { endConversation(beforeRedirect); } } else if ( isEndAnnotation ) { String[] outcomes = invocationContext.getAccessibleObject().getAnnotation(End.class).ifOutcome(); if ( Arrays.asList(outcomes).contains(result) ) { endConversation(beforeRedirect); } } else if ( isEndTaskAnnotation ) { //TODO: fix minor code duplication String[] outcomes = invocationContext.getAccessibleObject().getAnnotation(EndTask.class).ifOutcome(); if ( Arrays.asList(outcomes).contains(result) ) { endConversation(beforeRedirect); } } } private void endConversation(boolean beforeRedirect) { Manager.instance().endConversation(beforeRedirect); } }