com.ocpsoft.pretty.PrettyContext Maven / Gradle / Ivy
/*
* Copyright 2010 Lincoln Baxter, III
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ocpsoft.pretty;
import java.io.IOException;
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.context.FacesContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.ocpsoft.pretty.faces.config.PrettyConfig;
import com.ocpsoft.pretty.faces.config.PrettyConfigurator;
import com.ocpsoft.pretty.faces.config.mapping.UrlMapping;
import com.ocpsoft.pretty.faces.url.QueryString;
import com.ocpsoft.pretty.faces.url.URL;
import com.ocpsoft.pretty.faces.util.Assert;
/**
* @author Lincoln Baxter, III
*/
public class PrettyContext implements Serializable
{
private static final String DEFAULT_ENCODING = "UTF-8";
public static final String CONFIG_KEY = "com.ocpsoft.pretty.CONFIG_FILES";
private static final Log log = LogFactory.getLog(PrettyContext.class);
private static final long serialVersionUID = -4593906924975844541L;
public static final String PRETTY_PREFIX = "pretty:";
private static final String CONTEXT_REQUEST_KEY = "prettyContext";
private static final Pattern JSESSIONID_PATTERN = Pattern.compile("(?i)^(.*);jsessionid=[\\w\\.\\-]+(.*)");
private static final String JSESSIONID_REPLACEMENT = "$1$2";
private PrettyConfig config;
private final String contextPath;
private URL requestURL;
private final QueryString requestQuery;
private UrlMapping currentMapping;
private boolean dynaviewProcessed = false;
private boolean inNavigation = false;
/**
* Must create instance through the initialize() method
*/
protected PrettyContext(final HttpServletRequest request)
{
Assert.notNull(request, "HttpServletRequest argument was null");
// attribute is set by PrettyFacesRewriteLifecycleListener before
config = (PrettyConfig) request.getAttribute(CONFIG_KEY);
// not sure if this can happen any more, but we'll keep it for now
if (config == null)
{
config = new PrettyConfig();
}
contextPath = request.getContextPath();
String requestUrl = stripContextPath(request.getRequestURI());
Matcher sessionIdMatcher = JSESSIONID_PATTERN.matcher(requestUrl);
if (sessionIdMatcher.matches())
{
requestUrl = sessionIdMatcher.replaceFirst(JSESSIONID_REPLACEMENT);
}
String encoding = request.getCharacterEncoding() == null ? DEFAULT_ENCODING : request.getCharacterEncoding();
requestURL = new URL(requestUrl);
requestURL.setEncoding(encoding);
requestURL = requestURL.decode();
requestQuery = QueryString.build(request.getQueryString());
log.trace("Initialized PrettyContext");
}
/**
* Get the current PrettyFaces context object, or construct a new one if it
* does not yet exist for this request. (Delegates to
* {@link FacesContext#getCurrentInstance()} to retrieve the current
* {@link HttpServletRequest} object)
*
* @return current context instance
*/
public static PrettyContext getCurrentInstance()
{
FacesContext context = FacesContext.getCurrentInstance();
return getCurrentInstance(context);
}
/**
* Get the current PrettyFaces context object, or construct a new one if it
* does not yet exist for this request. (Delegates to {@link FacesContext} to
* retrieve the current {@link HttpServletRequest} object)
*
* @return current context instance
*/
public static PrettyContext getCurrentInstance(final FacesContext context)
{
Assert.notNull(context, "FacesContext argument was null.");
return getCurrentInstance((HttpServletRequest) context.getExternalContext().getRequest());
}
/**
* Get the current {@link PrettyContext}, or construct a new one if it does
* not yet exist for this request.
*
* @return current context instance
*/
public static PrettyContext getCurrentInstance(final HttpServletRequest request)
{
Assert.notNull(request, "HttpServletRequest argument was null");
PrettyContext prettyContext = (PrettyContext) request.getAttribute(CONTEXT_REQUEST_KEY);
if (prettyContext instanceof PrettyContext)
{
log.trace("Retrieved PrettyContext from Request");
return prettyContext;
}
else
{
Assert.notNull(request, "HttpServletRequest argument was null");
prettyContext = newDetachedInstance(request);
log.trace("PrettyContext not found in Request - building new instance");
setCurrentContext(request, prettyContext);
return prettyContext;
}
}
/**
* Return true if there is an instantiated {@link PrettyContext} contained in
* the current given request object.
*/
public static boolean isInstantiated(final ServletRequest request)
{
Assert.notNull(request, "HttpServletRequest argument was null");
PrettyContext prettyContext = (PrettyContext) request.getAttribute(CONTEXT_REQUEST_KEY);
if (prettyContext instanceof PrettyContext)
{
return true;
}
else
{
return false;
}
}
/**
* Package private -- only {@link PrettyFilter} should be calling this method
* -- it does not overwrite existing contexts in Request object
*/
public static PrettyContext newDetachedInstance(final HttpServletRequest request)
{
Assert.notNull(request, "HttpServletRequest argument was null");
PrettyContext prettyContext = new PrettyContext(request);
return prettyContext;
}
/**
* Package private -- only PrettyFilter or this class should be calling this
* method -- it overwrites existing contexts in Request object
*/
public static void setCurrentContext(final HttpServletRequest request, final PrettyContext prettyContext)
{
request.setAttribute(CONTEXT_REQUEST_KEY, prettyContext);
}
/**
* Return the {@link URL} representing the path with which this request was
* populated. (Does not include context-path.)
*/
public URL getRequestURL()
{
return requestURL;
}
/**
* Return the {@link QueryString} representing the query-string with which
* this request was populated.
*
* @return
*/
public QueryString getRequestQueryString()
{
return requestQuery;
}
/**
* Determine if this request URL is mapped by PrettyFaces
*/
public boolean isPrettyRequest()
{
return getCurrentMapping() != null;
}
public String getContextPath()
{
return contextPath;
}
/**
* If the given URL is prefixed with this request's context-path, return the
* URI without the context path. Otherwise return the URI unchanged.
*/
public String stripContextPath(String uri)
{
if (uri.startsWith(contextPath))
{
uri = uri.substring(contextPath.length());
}
return uri;
}
/**
* Get the pretty-config.xml configurations as loaded by
* {@link PrettyConfigurator} (This can be dynamically manipulated at runtime
* in order to change or add any mappings.
*/
public PrettyConfig getConfig()
{
return config;
}
void setConfig(final PrettyConfig config)
{
this.config = config;
}
/**
* Get the {@link UrlMapping} representing the current request.
*/
public UrlMapping getCurrentMapping()
{
if (currentMapping == null)
{
currentMapping = config.getMappingForUrl(requestURL);
}
return currentMapping;
}
/**
* Called once from PrettyFilter to initialize the currentMapping property.
* This leads to better performance because the filter already knows the mapping
* and we won't have to do the mapping identification again in the getter for
* the property.
*/
void setCurrentMapping(UrlMapping mapping)
{
currentMapping = mapping;
}
/**
* Return the current viewId to which the current request will be forwarded
* to JSF.
*/
public String getCurrentViewId()
{
if (getCurrentMapping() != null)
{
return currentMapping.getViewId();
}
return "";
}
/**
* Return whether or not this faces application is in the Navigation State
*/
public boolean isInNavigation()
{
return inNavigation;
}
/**
* Set whether or not to treat this request as if it is in the Navigation
* State
*/
public void setInNavigation(final boolean value)
{
inNavigation = value;
}
/**
* @return True if the current mapping and request should trigger DynaView
* capabilities.
*/
public boolean shouldProcessDynaview()
{
if (isPrettyRequest())
{
return getCurrentMapping().isDynaView() && !isDynaviewProcessed();
}
return false;
}
/**
* Return true if the current request has already processed the DynaView
* life-cycle.
*/
public boolean isDynaviewProcessed()
{
return dynaviewProcessed;
}
public void setDynaviewProcessed(final boolean value)
{
dynaviewProcessed = value;
}
public String getDynaViewId()
{
return config.getDynaviewId();
}
/**
*
* Sends an error response to the client using the specified HTTP status
* code.
*
*
* Please note that this method can only be called from within the JSF
* lifecycle as it needs the {@link FacesContext} to obtain the
* {@link HttpServletResponse}. Please use
* {@link #sendError(int, String, HttpServletResponse)} in all other
* cases.
*
*
* @param code the error status code
* @see HttpServletResponse#sendError(int, String)
*/
public void sendError(int code)
{
sendError(code, null);
}
/**
*
* Sends an error response to the client using the specified HTTP status
* code.
*
*
* Please note that this method can only be called from within the JSF
* lifecycle as it needs the {@link FacesContext} to obtain the
* {@link HttpServletResponse}. Please use
* {@link #sendError(int, String, HttpServletResponse)} in all other
* cases.
*
*
* @param code the error status code
* @param message the descriptive message
* @see HttpServletResponse#sendError(int, String)
*/
public void sendError(int code, String message)
{
FacesContext facesContext = FacesContext.getCurrentInstance();
Assert.notNull(facesContext, "FacesContext argument was null.");
Object response = facesContext.getExternalContext().getResponse();
if (response instanceof HttpServletResponse)
{
sendError(code, message, (HttpServletResponse) response);
facesContext.responseComplete();
}
}
/**
* Sends an error response to the client using the specified HTTP status
* code.
*
* @param code the error status code
* @param message the descriptive message
* @param response
* @see HttpServletResponse#sendError(int, String)
*/
public void sendError(int code, String message, HttpServletResponse response)
{
Assert.notNull(response, "HttpServletResponse argument was null");
try
{
if (message != null)
{
response.sendError(code, message);
}
else
{
response.sendError(code);
}
}
catch (IOException e)
{
throw new IllegalStateException("Failed to send error code: " + code, e);
}
}
}