org.frameworkset.web.servlet.view.InternalResourceView Maven / Gradle / Ivy
Show all versions of bboss-mvc Show documentation
/*
* Copyright 2008 biaoping.yin
*
* 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 org.frameworkset.web.servlet.view;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.frameworkset.web.servlet.support.ContextExposingHttpServletRequest;
import org.frameworkset.web.servlet.support.RequestMethodHttpServletRequest;
import org.frameworkset.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.frameworkset.util.StringUtil;
/**
* Title: InternalResourceView.java
* Description:
* bboss workgroup
* Copyright (c) 2008
* @Date 2010-9-28
* @author biaoping.yin
* @version 1.0
*/
public class InternalResourceView extends AbstractUrlBasedView {
private static Logger logger = LoggerFactory.getLogger(InternalResourceView.class);
private boolean alwaysInclude = false;
private Boolean exposeForwardAttributes;
private boolean exposeContextBeansAsAttributes = false;
private Set exposedContextBeanNames;
private boolean preventDispatchLoop = false;
/**
* Constructor for use as a bean.
* @see #setUrl
* @see #setAlwaysInclude
*/
public InternalResourceView() {
}
/**
* Create a new InternalResourceView with the given URL.
* @param url the URL to forward to
* @see #setAlwaysInclude
*/
public InternalResourceView(String url) {
super(url);
}
/**
* Create a new InternalResourceView with the given URL.
* @param url the URL to forward to
* @param alwaysInclude whether to always include the view rather than forward to it
*/
public InternalResourceView(String url, boolean alwaysInclude) {
super(url);
this.alwaysInclude = alwaysInclude;
}
/**
* Specify whether to always include the view rather than forward to it.
* Default is "false". Switch this flag on to enforce the use of a
* Servlet include, even if a forward would be possible.
* @see javax.servlet.RequestDispatcher#forward
* @see javax.servlet.RequestDispatcher#include
* @see #useInclude(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public void setAlwaysInclude(boolean alwaysInclude) {
this.alwaysInclude = alwaysInclude;
}
/**
* Set whether to explictly expose the Servlet 2.4 forward request attributes
* when forwarding to the underlying view resource.
*
Default is "true" on Servlet containers up until 2.4, and "false" for
* Servlet 2.5 and above. Note that Servlet containers at 2.4 level and above
* should expose those attributes automatically! This InternalResourceView
* feature exists for Servlet 2.3 containers and misbehaving 2.4 containers.
*/
public void setExposeForwardAttributes(boolean exposeForwardAttributes) {
this.exposeForwardAttributes = Boolean.valueOf(exposeForwardAttributes);
}
/**
* Set whether to make all Bboss beans in the application context accessible
* as request attributes, through lazy checking once an attribute gets accessed.
*
This will make all such beans accessible in plain ${...}
* expressions in a JSP 2.0 page, as well as in JSTL's c:out
* value expressions.
*
Default is "false". Switch this flag on to transparently expose all
* Bboss beans in the request attribute namespace.
*
NOTE: Context beans will override any custom request or session
* attributes of the same name that have been manually added. However, model
* attributes (as explicitly exposed to this view) of the same name will
* always override context beans.
* @see #getRequestToExpose
*/
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
}
/**
* Specify the names of beans in the context which are supposed to be exposed.
* If this is non-null, only the specified beans are eligible for exposure as
* attributes.
*
If you'd like to expose all Bboss beans in the application context, switch
* the {@link #setExposeContextBeansAsAttributes "exposeContextBeansAsAttributes"}
* flag on but do not list specific bean names for this property.
*/
public void setExposedContextBeanNames(String[] exposedContextBeanNames) {
this.exposedContextBeanNames = new HashSet(Arrays.asList(exposedContextBeanNames));
}
/**
* Set whether to explicitly prevent dispatching back to the
* current handler path.
*
Default is "false". Switch this to "true" for convention-based
* views where a dispatch back to the current handler path is a
* definitive error.
*/
public void setPreventDispatchLoop(boolean preventDispatchLoop) {
this.preventDispatchLoop = preventDispatchLoop;
}
/**
* An ApplicationContext is not strictly required for InternalResourceView.
*/
protected boolean isContextRequired() {
return false;
}
/**
* Checks whether we need explictly expose the Servlet 2.4 request attributes
* by default.
* @see #setExposeForwardAttributes
* @see #exposeForwardRequestAttributes(javax.servlet.http.HttpServletRequest)
*/
protected void initServletContext(ServletContext sc) {
if (this.exposeForwardAttributes == null && sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
this.exposeForwardAttributes = Boolean.TRUE;
}
}
/**
* Render the internal resource given the specified model.
* This includes setting the model as request attributes.
*/
protected void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if(request instanceof RequestMethodHttpServletRequest)
{
((RequestMethodHttpServletRequest)request).setDelegateGetMethod(false);
}
// Determine which request handle to expose to the RequestDispatcher.
HttpServletRequest requestToExpose = getRequestToExpose(request);
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, requestToExpose);
// Expose helpers as request attributes, if any.
exposeHelpers(requestToExpose);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(requestToExpose, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = requestToExpose.getRequestDispatcher(dispatcherPath);
if (rd == null) {
throw new ServletException(
"Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(requestToExpose, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(requestToExpose, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
exposeForwardRequestAttributes(requestToExpose);
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.forward(requestToExpose, response);
}
}
/**
* Expose helpers unique to each rendering operation. This is necessary so that
* different rendering operations can't overwrite each other's contexts etc.
*
Called by {@link #renderMergedOutputModel(Map, HttpServletRequest, HttpServletResponse)}.
* The default implementation is empty. This method can be overridden to add
* custom helpers as request attributes.
* @param request current HTTP request
* @throws Exception if there's a fatal error while we're adding attributes
* @see #renderMergedOutputModel
* @see JstlView#exposeHelpers
* @see tiles.TilesJstlView#exposeHelpers
*/
protected void exposeHelpers(HttpServletRequest request) throws Exception {
}
/**
* Prepare for rendering, and determine the request dispatcher path
* to forward to (or to include).
*
This implementation simply returns the configured URL.
* Subclasses can override this to determine a resource to render,
* typically interpreting the URL in a different manner.
* @param request current HTTP request
* @param response current HTTP response
* @return the request dispatcher path to use
* @throws Exception if preparations failed
* @see #getUrl()
* @see tiles.TilesView#prepareForRendering
*/
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
throws Exception {
String path = getUrl();
if (this.preventDispatchLoop) {
String uri = request.getRequestURI();
if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtil.applyRelativePath(uri, path))) {
throw new ServletException("Circular view path [" + path + "]: would dispatch back " +
"to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +
"(Hint: This may be the result of an unspecified view, due to default view name generation.)");
}
}
return path;
}
/**
* Get the request handle to expose to the RequestDispatcher, i.e. to the view.
*
The default implementation wraps the original request for exposure of
* Bboss beans as request attributes (if demanded).
* @param originalRequest the original servlet request as provided by the engine
* @return the wrapped request, or the original request if no wrapping is necessary
* @see #setExposeContextBeansAsAttributes
*/
protected HttpServletRequest getRequestToExpose(HttpServletRequest originalRequest) {
if (this.exposeContextBeansAsAttributes || this.exposedContextBeanNames != null) {
return new ContextExposingHttpServletRequest(
originalRequest, getWebApplicationContext(), this.exposedContextBeanNames);
}
return originalRequest;
}
/**
* Determine whether to use RequestDispatcher's include
or
* forward
method.
*
Performs a check whether an include URI attribute is found in the request,
* indicating an include request, and whether the response has already been committed.
* In both cases, an include will be performed, as a forward is not possible anymore.
* @param request current HTTP request
* @param response current HTTP response
* @return true
for include, false
for forward
* @see javax.servlet.RequestDispatcher#forward
* @see javax.servlet.RequestDispatcher#include
* @see javax.servlet.ServletResponse#isCommitted
* @see WebUtils#isIncludeRequest
*/
protected boolean useInclude(HttpServletRequest request, HttpServletResponse response) {
return (this.alwaysInclude || WebUtils.isIncludeRequest(request) || response.isCommitted());
}
/**
* Expose the current request URI and paths as {@link HttpServletRequest}
* attributes under the keys defined in the Servlet 2.4 specification,
* for Servlet 2.3 containers as well as misbehaving Servlet 2.4 containers
* (such as OC4J).
*
Does not expose the attributes on Servlet 2.5 or above, mainly for
* GlassFish compatibility (GlassFish gets confused by pre-exposed attributes).
* In any case, Servlet 2.5 containers should finally properly support
* Servlet 2.4 features, shouldn't they...
* @param request current HTTP request
* @see WebUtils#exposeForwardRequestAttributes
*/
protected void exposeForwardRequestAttributes(HttpServletRequest request) {
if (this.exposeForwardAttributes != null && this.exposeForwardAttributes.booleanValue()) {
WebUtils.exposeForwardRequestAttributes(request);
}
}
}