package org.jboss.seam.core;
import static org.jboss.seam.annotations.Install.BUILT_IN;
import java.io.Serializable;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.FlushModeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.persistence.PersistenceContexts;
/**
* Allows the conversation timeout to be set per-conversation,
* and the conversation description and switchable outcome to
* be set when the application requires workspace management
* functionality.
*
* @author Gavin King
*
*/
@Scope(ScopeType.CONVERSATION)
@Name("org.jboss.seam.core.conversation")
@Install(precedence=BUILT_IN)
@BypassInterceptors
public class Conversation implements Serializable
{
private static final long serialVersionUID = -6131304128727444876L;
private Integer timeout;
private Integer concurrentRequestTimeout;
String description;
String viewId;
/**
* Kills all conversations except the current one
*/
public void killAllOthers()
{
Manager.instance().killAllOtherConversations();
}
/**
* Get the timeout for this conversation instance.
* @return the timeout in millis
*/
public Integer getTimeout()
{
return timeout==null ?
Manager.instance().getCurrentConversationTimeout() :
timeout;
}
/**
* Set the timeout for this converstaion instance.
* @param timeout the timeout in millis
*/
public void setTimeout(Integer timeout)
{
this.timeout = timeout;
}
public Integer getConcurrentRequestTimeout()
{
return concurrentRequestTimeout == null ? Manager.instance().getCurrentConversationConcurrentRequestTimeout() : concurrentRequestTimeout;
}
public void setConcurrentRequestTimeout(Integer concurrentRequestTimeout)
{
this.concurrentRequestTimeout = concurrentRequestTimeout;
}
/**
* Get the conversation id.
*/
public String getId()
{
return Manager.instance().getCurrentConversationId();
}
public String getDescription()
{
return description==null ?
Manager.instance().getCurrentConversationDescription() :
description;
}
public String getViewId()
{
return viewId==null ?
Manager.instance().getCurrentConversationViewId() :
viewId;
}
/**
* Sets the description of this conversation, for use
* in the conversation list, breadcrumbs, or conversation
* switcher.
*/
public void setDescription(String description)
{
this.description = description;
}
/**
* Sets the JSF outcome to be used when we switch back to this
* conversation from the conversation list, breadcrumbs, or
* conversation switcher.
*/
public void setViewId(String outcome)
{
this.viewId = outcome;
}
public static Conversation instance()
{
if ( !Contexts.isConversationContextActive() )
{
throw new IllegalStateException("No active conversation context");
}
return (Conversation) Component.getInstance(Conversation.class, ScopeType.CONVERSATION);
}
void flush()
{
//we need to flush this stuff asynchronously to handle
//nested and temporary conversations which have no
//ConversationEntry
Manager manager = Manager.instance();
if ( !manager.isLongRunningConversation() )
{
throw new IllegalStateException("only long-running conversation outcomes are switchable");
}
ConversationEntry entry = manager.getCurrentConversationEntry();
if (entry==null)
{
throw new IllegalStateException("missing conversation entry"); //should never happen
}
if (viewId!=null)
{
entry.setViewId(viewId);
}
if (description!=null)
{
entry.setDescription(description);
}
if (timeout!=null)
{
entry.setTimeout(timeout);
}
if (concurrentRequestTimeout != null)
{
entry.setConcurrentRequestTimeout(concurrentRequestTimeout);
}
description = null;
viewId = null;
timeout = null;
}
/**
* Switch back to the last defined view-id for the
* current conversation.
*
* @return true if a redirect occurred
*/
public boolean redirect()
{
Manager manager = Manager.instance();
return redirect( manager, manager.getCurrentConversationViewId() );
}
private boolean redirect(Manager manager, String viewId)
{
if (viewId==null)
{
return false;
}
else
{
manager.redirect(viewId);
return true;
}
}
/**
* End a child conversation and redirect to the last defined
* view-id for the parent conversation.
*
* @return true if a redirect occurred
*/
public boolean endAndRedirect()
{
return endAndRedirect(false);
}
/**
* End a child conversation and redirect to the last defined
* view-id for the parent conversation.
*
* @param endBeforeRedirect should the conversation be destroyed before the redirect?
* @return true if a redirect occurred
*/
public boolean endAndRedirect(boolean endBeforeRedirect)
{
end(endBeforeRedirect);
Manager manager = Manager.instance();
return redirect( manager, manager.getParentConversationViewId() );
}
/**
* Leave the scope of the current conversation
*/
public void leave()
{
Manager.instance().leaveConversation();
}
/**
* Start a long-running conversation, if no long-running
* conversation is active.
*
* @return true if a new long-running conversation was begun
*/
public boolean begin()
{
if ( Manager.instance().isLongRunningOrNestedConversation() )
{
return false;
}
else
{
reallyBegin();
return true;
}
}
private void reallyBegin()
{
Manager.instance().beginConversation( );
}
/**
* Start a nested conversation.
*
* @throws IllegalStateException if no long-running conversation was active
*/
public void beginNested()
{
if ( Manager.instance().isLongRunningConversation() )
{
Manager.instance().beginNestedConversation( );
}
else
{
throw new IllegalStateException("beginNested() called with no long-running conversation");
}
}
/**
* Begin or join a conversation, or begin a new nested conversation.
*
* @param join if a conversation is active, should we join it?
* @param nested if a conversation is active, should we start a new nested conversation?
* @return true if a new long-running conversation was begun
*/
public boolean begin(boolean join, boolean nested)
{
boolean longRunningConversation = Manager.instance().isLongRunningOrNestedConversation();
if ( !join && !nested && longRunningConversation )
{
throw new IllegalStateException("begin() called from long-running conversation, try join=true");
}
else if ( !longRunningConversation )
{
reallyBegin();
return true;
}
else if (nested)
{
beginNested();
return true;
}
else
{
return false;
}
}
/**
* End a long-runnning conversation.
*/
public void end()
{
end(false);
}
/**
* End a long-runnning conversation and destroy
* it before a redirect.
*/
public void endBeforeRedirect()
{
end(true);
}
/**
* End a long-runnning conversation.
*
* @param beforeRedirect should the conversation be destroyed before any redirect?
*/
public void end(boolean beforeRedirect)
{
Manager.instance().endConversation(beforeRedirect);
}
/**
* Is this conversation long-running? Note that this method returns
* false even when the conversation has been temporarily promoted
* to long-running for the course of a redirect, so it does what
* the user really expects.
*/
public boolean isLongRunning()
{
return Manager.instance().isReallyLongRunningConversation();
}
/**
* Is this conversation a nested conversation?
*/
public boolean isNested()
{
return Manager.instance().isNestedConversation();
}
/**
* Get the id of the immediate parent of a nested conversation
*/
public String getParentId()
{
return Manager.instance().getParentConversationId();
}
/**
* Get the id of root conversation of a nested conversation
*/
public String getRootId()
{
return Manager.instance().getRootConversationId();
}
/**
* "Pop" the conversation stack, switching to the parent conversation
*/
public void pop()
{
String parentId = getParentId();
if (parentId!=null)
{
Manager.instance().switchConversation(parentId);
}
}
/**
* Pop the conversation stack and redirect to the last defined
* view-id for the parent conversation.
*
* @return true if a redirect occurred
*/
public boolean redirectToParent()
{
pop();
return redirect();
}
/**
* Switch to the root conversation
*/
public void root()
{
String rootId = getRootId();
if (rootId!=null)
{
Manager.instance().switchConversation(rootId);
}
}
/**
* Switch to the root conversation and redirect to the
* last defined view-id for the root conversation.
*
* @return true if a redirect occurred
*/
public boolean redirectToRoot()
{
root();
return redirect();
}
/**
* Change the flush mode of all Seam-managed persistence
* contexts in this conversation.
*/
public void changeFlushMode(FlushModeType flushMode)
{
PersistenceContexts.instance().changeFlushMode(flushMode);
}
}