package org.jboss.seam.navigation;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;
import org.jboss.seam.core.Events;
import org.jboss.seam.core.Interpolator;
import org.jboss.seam.core.ResourceLoader;
import org.jboss.seam.core.Expressions.ValueExpression;
import org.jboss.seam.security.Identity;
import org.jboss.seam.util.Strings;
import org.jboss.seam.web.Pattern;
/**
* Metadata about page actions, page parameters, action navigation,
* resource bundle, etc, for a particular JSF view id.
*/
public final class Page
{
public static final String RFC_1123_DATE = "EEE, dd MMM yyyy HH:mm:ss zzz";
private final String viewId;
private String description;
private Integer timeout;
private Integer concurrentRequestTimeout;
private ValueExpression<String> noConversationViewId;
private String resourceBundleName;
private boolean switchEnabled = true;
private List<Param> parameters = new ArrayList<Param>();
private List<Input> inputs = new ArrayList<Input>();
private List<Action> actions = new ArrayList<Action>();
private Map<String, Navigation> navigations = new HashMap<String, Navigation>();
private Navigation defaultNavigation;
private boolean conversationRequired;
private boolean loginRequired;
private ConversationControl conversationControl = new ConversationControl();
private TaskControl taskControl = new TaskControl();
private ProcessControl processControl = new ProcessControl();
private ConversationIdParameter conversationIdParameter;
private List<String> eventTypes = new ArrayList<String>();
private List<Pattern> rewritePatterns = new ArrayList<Pattern>();
private List<Header> httpHeaders = new ArrayList<Header>();
private Integer expires;
/**
* The scheme (http/https) required by this page.
*/
private String scheme;
/**
* Indicates whether this view id has a security restriction.
*/
private boolean restricted;
/**
* A security restriction expression to evaluate when requesting this view id.
* If the view is restricted but no restriction expression is set, the implied
* permission restriction will be name="[viewid]", action="[get or post]"
*/
private String restriction;
public Page(String viewId)
{
this.viewId = viewId;
if (viewId!=null)
{
if (viewId.equals("/debug.xhtml"))
{
switchEnabled = false;
}
int loc = viewId.lastIndexOf('.');
if ( loc>0 && viewId.startsWith("/") )
{
this.setResourceBundleName( viewId.substring(1, loc) );
}
}
conversationIdParameter = new SyntheticConversationIdParameter();
}
public java.util.ResourceBundle getResourceBundle()
{
String resourceBundleName = getResourceBundleName();
if (resourceBundleName==null)
{
return null;
}
else
{
return ResourceLoader.instance().loadBundle(resourceBundleName);
}
}
@Override
public String toString()
{
return "Page(" + getViewId() + ")";
}
public String getViewId()
{
return viewId;
}
public String renderDescription()
{
return Interpolator.instance().interpolate( getDescription() );
}
public void setDescription(String description)
{
this.description = description;
}
public String getDescription()
{
return description;
}
public void setTimeout(Integer timeout)
{
this.timeout = timeout;
}
public Integer getTimeout()
{
return timeout;
}
public void setConcurrentRequestTimeout(Integer concurrentRequestTimeout)
{
this.concurrentRequestTimeout = concurrentRequestTimeout;
}
public Integer getConcurrentRequestTimeout()
{
return concurrentRequestTimeout;
}
public void setNoConversationViewId(ValueExpression<String> noConversationViewId)
{
this.noConversationViewId = noConversationViewId;
}
public ValueExpression<String> getNoConversationViewId()
{
return noConversationViewId;
}
public void setResourceBundleName(String resourceBundleName)
{
this.resourceBundleName = resourceBundleName;
}
public String getResourceBundleName()
{
return resourceBundleName;
}
public void setSwitchEnabled(boolean switchEnabled)
{
this.switchEnabled = switchEnabled;
}
public boolean isSwitchEnabled()
{
return switchEnabled;
}
public List<Param> getParameters()
{
return parameters;
}
public Map<String, Navigation> getNavigations()
{
return navigations;
}
public boolean hasDescription()
{
return description!=null;
}
public boolean isConversationRequired()
{
return conversationRequired;
}
public void setConversationRequired(boolean conversationRequired)
{
this.conversationRequired = conversationRequired;
}
public Navigation getDefaultNavigation()
{
return defaultNavigation;
}
public void setDefaultNavigation(Navigation defaultActionOutcomeMapping)
{
this.defaultNavigation = defaultActionOutcomeMapping;
}
public ConversationControl getConversationControl()
{
return conversationControl;
}
public TaskControl getTaskControl()
{
return taskControl;
}
public ProcessControl getProcessControl()
{
return processControl;
}
public List<Action> getActions()
{
return actions;
}
private void checkPermission(FacesContext facesContext, String name)
{
if ( isRestricted() && Identity.isSecurityEnabled() )
{
// If no expression is configured, create a default one
if (restriction == null)
{
Identity.instance().checkPermission( Pages.getViewId(facesContext), name );
}
else
{
Identity.instance().checkRestriction(restriction);
}
}
}
/**
* Check the restore permission.
*/
public void postRestore(FacesContext facesContext)
{
checkPermission(facesContext, "restore");
}
/**
* Call page actions, in order they appear in XML, and
* handle conversation begin/end. Also check the
* render permission.
*/
public boolean preRender(FacesContext facesContext)
{
checkPermission(facesContext, "render");
sendHeaders(facesContext);
boolean result = false;
getConversationControl().beginOrEndConversation();
getTaskControl().beginOrEndTask();
getProcessControl().createOrResumeProcess();
for ( Input in: getInputs() ) in.in();
for (String eventType : eventTypes)
{
Events.instance().raiseEvent(eventType);
}
for ( Action action: getActions() )
{
if ( action.isExecutable(facesContext.getRenderKit().getResponseStateManager().isPostback(facesContext)) )
{
String outcome = action.getOutcome();
String fromAction = outcome;
if (outcome==null)
{
fromAction = action.getMethodExpression().getExpressionString();
result = true;
outcome = Pages.toString( action.getMethodExpression().invoke() );
UIViewRoot oldViewRoot = facesContext.getViewRoot();
Pages.handleOutcome(facesContext, outcome, fromAction);
if (facesContext.getResponseComplete() || oldViewRoot != facesContext.getViewRoot()) {
break;
}
}
else
{
UIViewRoot oldViewRoot = facesContext.getViewRoot();
Pages.handleOutcome(facesContext, outcome, fromAction);
if (facesContext.getResponseComplete() || oldViewRoot != facesContext.getViewRoot()) {
break;
}
}
}
}
return result;
}
private void sendHeaders(FacesContext facesContext) {
Object value = facesContext.getExternalContext().getResponse();
if (value == null || !(value instanceof HttpServletResponse)) {
return;
}
HttpServletResponse response = (HttpServletResponse) value;
for (Header header: httpHeaders) {
header.sendHeader(response);
}
if (expires != null) {
Header.sendHeader(response,
"Expires",
rfc1123Date(new Date(System.currentTimeMillis()+expires*1000)));
}
}
private String rfc1123Date(Date when) {
SimpleDateFormat format = new SimpleDateFormat(RFC_1123_DATE);
return format.format(when);
}
public List<Input> getInputs()
{
return inputs;
}
public boolean isRestricted()
{
return restricted;
}
public void setRestricted(boolean restricted)
{
this.restricted = restricted;
}
public String getRestriction()
{
return restriction;
}
public void setRestriction(String restriction)
{
this.restriction = restriction;
}
public boolean isLoginRequired()
{
return loginRequired;
}
public void setLoginRequired(boolean loginRequired)
{
this.loginRequired = loginRequired;
}
public String getScheme()
{
return scheme;
}
public void setScheme(String scheme)
{
this.scheme = scheme;
}
public ConversationIdParameter getConversationIdParameter()
{
return conversationIdParameter;
}
public void setConversationIdParameter(ConversationIdParameter param)
{
this.conversationIdParameter = param;
}
public List<String> getEventTypes()
{
return eventTypes;
}
public void addEventType(String eventType)
{
if (!Strings.isEmpty(eventType))
{
eventTypes.add(eventType);
}
}
public List<Pattern> getRewritePatterns()
{
return rewritePatterns;
}
//public void setRewritePatterns(List<String> rewritePatterns) {
// this.rewritePatterns = rewritePatterns;
//}
public void addRewritePattern(String value) {
Pattern pattern = new Pattern(viewId, value);
rewritePatterns.add(pattern);
}
public List<Header> getHeaders() {
return httpHeaders;
}
public void setExpires(Integer expires) {
this.expires = expires;
}
}