/*
* Copyright 2004 The Apache Software Foundation.
*
* 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.apache.myfaces.application.jsp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.portlet.MyFacesGenericPortlet;
import org.apache.myfaces.portlet.PortletUtil;
import org.apache.myfaces.util.DebugUtils;
import org.apache.myfaces.shared_impl.webapp.webxml.ServletMapping;
import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.render.RenderKitFactory;
import javax.portlet.PortletURL;
import javax.portlet.RenderResponse;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
/**
* @author Thomas Spiegl (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class JspViewHandlerImpl
extends ViewHandler
{
private static final Log log = LogFactory.getLog(JspViewHandlerImpl.class);
public static final String FORM_STATE_MARKER = "<!--@@JSF_FORM_STATE_MARKER@@-->";
public static final int FORM_STATE_MARKER_LEN = FORM_STATE_MARKER.length();
public JspViewHandlerImpl()
{
if (log.isTraceEnabled()) log.trace("New ViewHandler instance created");
}
public Locale calculateLocale(FacesContext facesContext)
{
Iterator locales = facesContext.getExternalContext().getRequestLocales();
while (locales.hasNext())
{
Locale locale = (Locale)locales.next();
for (Iterator it = facesContext.getApplication().getSupportedLocales(); it.hasNext();)
{
Locale supportLocale = (Locale)it.next();
// higher priority to a language match over an exact match
// that occures further down (see Jstl Reference 1.0 8.3.1)
if (locale.getLanguage().equals(supportLocale.getLanguage()) &&
(supportLocale.getCountry() == null ||
supportLocale.getCountry().length() == 0))
{
return supportLocale;
}
else if (supportLocale.equals(locale))
{
return supportLocale;
}
}
}
Locale defaultLocale = facesContext.getApplication().getDefaultLocale();
return defaultLocale != null ? defaultLocale : Locale.getDefault();
}
public String calculateRenderKitId(FacesContext facesContext)
{
String renderKitId = facesContext.getApplication().getDefaultRenderKitId();
return (renderKitId!=null) ? renderKitId : RenderKitFactory.HTML_BASIC_RENDER_KIT;
//TODO: how to calculate from client?
}
/**
*/
public UIViewRoot createView(FacesContext facesContext, String viewId)
{
Application application = facesContext.getApplication();
ViewHandler applicationViewHandler = application.getViewHandler();
Locale currentLocale = null;
String currentRenderKitId = null;
UIViewRoot uiViewRoot = facesContext.getViewRoot();
if (uiViewRoot != null)
{
//Remember current locale and renderKitId
currentLocale = uiViewRoot.getLocale();
currentRenderKitId = uiViewRoot.getRenderKitId();
}
uiViewRoot = (UIViewRoot)application.createComponent(UIViewRoot.COMPONENT_TYPE);
// as of JSF spec page 7-16:
// "It is the callers responsibility to ensure that setViewId() is called
// on the returned view, passing the same viewId value."
// so we do not set the viewId here
// ok, but the RI does so, so let's do it, too.
uiViewRoot.setViewId(viewId);
if (currentLocale != null)
{
//set old locale
uiViewRoot.setLocale(currentLocale);
}
else
{
//calculate locale
uiViewRoot.setLocale(applicationViewHandler.calculateLocale(facesContext));
}
if (currentRenderKitId != null)
{
//set old renderKit
uiViewRoot.setRenderKitId(currentRenderKitId);
}
else
{
//calculate renderKit
uiViewRoot.setRenderKitId(applicationViewHandler.calculateRenderKitId(facesContext));
}
if (log.isTraceEnabled()) log.trace("Created view " + viewId);
return uiViewRoot;
}
public String getActionURL(FacesContext facesContext, String viewId)
{
if (PortletUtil.isRenderResponse(facesContext))
{
RenderResponse response = (RenderResponse)facesContext.getExternalContext().getResponse();
PortletURL url = response.createActionURL();
url.setParameter(MyFacesGenericPortlet.VIEW_ID, viewId);
return url.toString();
}
String path = getViewIdPath(facesContext, viewId);
if (path.length() > 0 && path.charAt(0) == '/')
{
return facesContext.getExternalContext().getRequestContextPath() + path;
}
else
{
return path;
}
}
public String getResourceURL(FacesContext facesContext, String path)
{
if (path.length() > 0 && path.charAt(0) == '/')
{
return facesContext.getExternalContext().getRequestContextPath() + path;
}
else
{
return path;
}
}
public void renderView(FacesContext facesContext, UIViewRoot viewToRender)
throws IOException, FacesException
{
if (viewToRender == null)
{
log.fatal("viewToRender must not be null");
throw new NullPointerException("viewToRender must not be null");
}
ExternalContext externalContext = facesContext.getExternalContext();
String viewId = facesContext.getViewRoot().getViewId();
if (PortletUtil.isPortletRequest(facesContext)) {
externalContext.dispatch(viewId);
return;
}
ServletMapping servletMapping = getServletMapping(externalContext);
if (servletMapping != null && servletMapping.isExtensionMapping())
{
String defaultSuffix = externalContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
String suffix = defaultSuffix != null ? defaultSuffix : ViewHandler.DEFAULT_SUFFIX;
DebugUtils.assertError(suffix.charAt(0) == '.',
log, "Default suffix must start with a dot!");
if (!viewId.endsWith(suffix))
{
int dot = viewId.lastIndexOf('.');
if (dot == -1)
{
if (log.isTraceEnabled()) log.trace("Current viewId has no extension, appending default suffix " + suffix);
viewId = viewId + suffix;
}
else
{
if (log.isTraceEnabled()) log.trace("Replacing extension of current viewId by suffix " + suffix);
viewId = viewId.substring(0, dot) + suffix;
}
facesContext.getViewRoot().setViewId(viewId);
}
}
if (log.isTraceEnabled()) log.trace("Dispatching to " + viewId);
// handle character encoding as of section 2.5.2.2 of JSF 1.1
if (externalContext.getResponse() instanceof ServletResponse) {
ServletResponse response = (ServletResponse) externalContext.getResponse();
response.setLocale(viewToRender.getLocale());
}
// TODO: 2.5.2.2 for Portlet? What do I do?
externalContext.dispatch(viewId);
// handle character encoding as of section 2.5.2.2 of JSF 1.1
if (externalContext.getRequest() instanceof HttpServletRequest) {
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
HttpSession session = request.getSession(false);
if (session != null) {
session.setAttribute(ViewHandler.CHARACTER_ENCODING_KEY, response.getCharacterEncoding());
}
}
}
public UIViewRoot restoreView(FacesContext facesContext, String viewId)
{
Application application = facesContext.getApplication();
ViewHandler applicationViewHandler = application.getViewHandler();
String renderKitId = applicationViewHandler.calculateRenderKitId(facesContext);
UIViewRoot viewRoot = application.getStateManager().restoreView(facesContext,
viewId,
renderKitId);
return viewRoot;
}
/**
* Writes a state marker that is replaced later by one or more hidden form
* inputs.
* @param facesContext
* @throws IOException
*/
public void writeState(FacesContext facesContext) throws IOException
{
if (facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext))
{
facesContext.getResponseWriter().write(FORM_STATE_MARKER);
}
}
protected String getViewIdPath(FacesContext facescontext, String viewId)
{
if (viewId == null)
{
log.error("ViewId must not be null");
throw new NullPointerException("ViewId must not be null");
}
if (!viewId.startsWith("/"))
{
log.error("ViewId must start with '/' (viewId = " + viewId + ")");
throw new IllegalArgumentException("ViewId must start with '/' (viewId = " + viewId + ")");
}
if (PortletUtil.isPortletRequest(facescontext))
{
return viewId;
}
ServletMapping servletMapping = getServletMapping(facescontext.getExternalContext());
if (servletMapping !=null)
{
if(servletMapping.isExtensionMapping())
{
// extension mapping
String urlpattern = servletMapping.getUrlPattern();
if (urlpattern.startsWith("*"))
{
urlpattern = urlpattern.substring(1, urlpattern.length());
}
if (viewId.endsWith(urlpattern))
{
return viewId;
}
else
{
int idx = viewId.lastIndexOf(".");
if (idx >= 0)
{
return viewId.substring(0, idx) + urlpattern;
}
else
{
return viewId + urlpattern;
}
}
}
else
{
// prefix mapping
String urlpattern = servletMapping.getUrlPattern();
if (urlpattern.endsWith("/*"))
{
urlpattern = urlpattern.substring(0, urlpattern.length() - 2);
}
return urlpattern + viewId;
}
}
else
{
return viewId;
}
}
private static ServletMapping getServletMapping(ExternalContext externalContext)
{
String servletPath = externalContext.getRequestServletPath();
String requestPathInfo = externalContext.getRequestPathInfo();
WebXml webxml = WebXml.getWebXml(externalContext);
List mappings = webxml.getFacesServletMappings();
if (requestPathInfo == null)
{
// might be extension mapping
for (int i = 0, size = mappings.size(); i < size; i++)
{
ServletMapping servletMapping = (ServletMapping) mappings.get(i);
String urlpattern = servletMapping.getUrlPattern();
String extension = urlpattern.substring(1, urlpattern.length());
if (servletPath.endsWith(extension))
{
return servletMapping;
} else if (servletPath.equals(urlpattern)) {
// path mapping with no pathInfo for the current request
return servletMapping;
}
}
}
else
{
// path mapping
for (int i = 0, size = mappings.size(); i < size; i++)
{
ServletMapping servletMapping = (ServletMapping) mappings.get(i);
String urlpattern = servletMapping.getUrlPattern();
urlpattern = urlpattern.substring(0, urlpattern.length() - 2);
// servletPath starts with "/" except in the case where the
// request is matched with the "/*" pattern, in which case
// it is the empty string (see Servlet Sepc 2.3 SRV4.4)
if (servletPath.equals(urlpattern))
{
return servletMapping;
}
}
}
// handle cases as best possible where servletPath is not a faces servlet,
// such as when coming through struts-faces
if (mappings.size() > 0)
{
return (ServletMapping) mappings.get(0);
}
else
{
log.warn("no faces servlet mappings found");
return null;
}
}
}