/* * Copyright 2004-2012 the original author or authors. * * 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.springframework.faces.webflow.context.portlet; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.el.ELContext; import javax.el.ELContextEvent; import javax.el.ELContextListener; import javax.el.ELResolver; import javax.el.FunctionMapper; import javax.el.VariableMapper; import javax.faces.application.Application; import javax.faces.application.ApplicationFactory; import javax.faces.application.FacesMessage; import javax.faces.application.ProjectStage; import javax.faces.component.UIViewRoot; import javax.faces.context.ExceptionHandler; import javax.faces.context.ExceptionHandlerFactory; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.PartialViewContext; import javax.faces.context.PartialViewContextFactory; import javax.faces.context.ResponseStream; import javax.faces.context.ResponseWriter; import javax.faces.event.PhaseId; import javax.faces.render.RenderKit; import javax.faces.render.RenderKitFactory; import javax.portlet.PortletContext; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import org.springframework.faces.webflow.JsfUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; /** * The default FacesContext implementation in Mojarra and in Apache MyFaces depends on the Servlet API. This * implementation provides an alternative that accepts Portlet request and response structures and creates a * {@link PortletExternalContextImpl} in its constructor. The rest of the method implementations mimic the equivalent * methods in the default FacesContext implementation. * * @author Rossen Stoyanchev * @author Phillip Webb * @since 2.2.0 */ public class PortletFacesContextImpl extends FacesContext { private static final List<FacesMessage> NO_MESSAGES = Collections.unmodifiableList(Collections .<FacesMessage> emptyList()); private static final Iterator<String> NO_CLIENT_IDS_WITH_MESSAGES = Collections.unmodifiableSet( Collections.<String> emptySet()).iterator(); private Application application; private ELContext elContext; private ExternalContext externalContext; private List<Message> messages; private FacesMessage.Severity maximumSeverity; private boolean released = false; private RenderKitFactory renderKitFactory; private boolean renderResponse = false; private boolean responseComplete = false; private ResponseStream responseStream; private ResponseWriter responseWriter; private UIViewRoot viewRoot; private Map<Object, Object> attributes; private PhaseId currentPhaseId; private ExceptionHandler exceptionHandler; private boolean validationFailed; private PartialViewContext partialViewContext; private boolean processingEvents; public PortletFacesContextImpl(ExternalContext externalContext) { this.externalContext = externalContext; } public PortletFacesContextImpl(PortletContext portletContext, PortletRequest portletRequest, PortletResponse portletResponse) { application = JsfUtils.findFactory(ApplicationFactory.class).getApplication(); renderKitFactory = JsfUtils.findFactory(RenderKitFactory.class); this.externalContext = new PortletExternalContextImpl(portletContext, portletRequest, portletResponse); FacesContext.setCurrentInstance(this); // This depends on the current FacesContext instance this.exceptionHandler = JsfUtils.findFactory(ExceptionHandlerFactory.class).getExceptionHandler(); } public void release() { assertNotReleased(); if (externalContext != null) { Method delegateMethod = ClassUtils.getMethodIfAvailable(externalContext.getClass(), "release"); if (delegateMethod != null) { try { delegateMethod.invoke(externalContext); } catch (Exception e) { externalContext.log("Failed to release external context", e); } externalContext = null; } } messages = null; maximumSeverity = null; application = null; responseStream = null; responseWriter = null; viewRoot = null; exceptionHandler = null; attributes = null; released = true; FacesContext.setCurrentInstance(null); } public ExternalContext getExternalContext() { assertNotReleased(); return externalContext; } public Application getApplication() { assertNotReleased(); return application; } public RenderKit getRenderKit() { if (getViewRoot() == null) { return null; } String renderKitId = getViewRoot().getRenderKitId(); if (renderKitId == null) { return null; } return renderKitFactory.getRenderKit(this, renderKitId); } public boolean getRenderResponse() { assertNotReleased(); return renderResponse; } public boolean getResponseComplete() { assertNotReleased(); return responseComplete; } public ResponseStream getResponseStream() { assertNotReleased(); return responseStream; } public void setResponseStream(ResponseStream responseStream) { assertNotReleased(); if (responseStream == null) { throw new NullPointerException("responseStream"); } this.responseStream = responseStream; } public ResponseWriter getResponseWriter() { assertNotReleased(); return responseWriter; } public void setResponseWriter(ResponseWriter responseWriter) { assertNotReleased(); if (responseWriter == null) { throw new NullPointerException("responseWriter"); } this.responseWriter = responseWriter; } public UIViewRoot getViewRoot() { assertNotReleased(); return viewRoot; } public void setViewRoot(UIViewRoot viewRoot) { assertNotReleased(); if (viewRoot == null) { throw new NullPointerException("viewRoot"); } this.viewRoot = viewRoot; } public void renderResponse() { assertNotReleased(); renderResponse = true; } public void responseComplete() { assertNotReleased(); responseComplete = true; } public ELContext getELContext() { if (elContext == null) { createELContext(); } return elContext; } private void createELContext() { elContext = new PortletELContextImpl(getApplication().getELResolver()); elContext.putContext(FacesContext.class, FacesContext.getCurrentInstance()); if (getViewRoot() != null) { elContext.setLocale(getViewRoot().getLocale()); } fireContextCreated(getApplication().getELContextListeners()); } private void fireContextCreated(ELContextListener[] listeners) { if (listeners.length > 0) { ELContextEvent event = new ELContextEvent(elContext); for (ELContextListener listener : listeners) { listener.contextCreated(event); } } } public void addMessage(String clientId, FacesMessage message) { assertNotReleased(); if (message == null) { throw new NullPointerException("message"); } if (messages == null) { messages = new ArrayList<Message>(); } messages.add(new Message(clientId, message)); recalculateMaximumSeverity(message.getSeverity()); } private void recalculateMaximumSeverity(FacesMessage.Severity severity) { if (severity != null) { if (maximumSeverity == null || severity.compareTo(maximumSeverity) > 0) { maximumSeverity = severity; } } } public FacesMessage.Severity getMaximumSeverity() { assertNotReleased(); return maximumSeverity; } public Iterator<FacesMessage> getMessages() { return getMessageList().iterator(); } public List<FacesMessage> getMessageList() { assertNotReleased(); if (messages == null || messages.isEmpty()) { return NO_MESSAGES; } List<FacesMessage> messageList = new ArrayList<FacesMessage>(); for (Message message : messages) { messageList.add(message.getFacesMessage()); } return Collections.unmodifiableList(messageList); } public Iterator<FacesMessage> getMessages(String clientId) { return getMessageList(clientId).iterator(); } public List<FacesMessage> getMessageList(String clientId) { assertNotReleased(); if (messages == null || messages.isEmpty()) { return NO_MESSAGES; } List<FacesMessage> messageList = new ArrayList<FacesMessage>(); for (Message message : messages) { if (message.isForClientId(clientId)) { messageList.add(message.getFacesMessage()); } } return Collections.unmodifiableList(messageList); } public Iterator<String> getClientIdsWithMessages() { assertNotReleased(); if (messages == null || messages.isEmpty()) { return NO_CLIENT_IDS_WITH_MESSAGES; } Set<String> clientIdsWithMessags = new LinkedHashSet<String>(); for (Message message : messages) { clientIdsWithMessags.add(message.getClientId()); } return Collections.unmodifiableSet(clientIdsWithMessags).iterator(); } public Map<Object, Object> getAttributes() { assertNotReleased(); if (attributes == null) { attributes = new HashMap<Object, Object>(); } return attributes; } public PhaseId getCurrentPhaseId() { assertNotReleased(); return currentPhaseId; } public ExceptionHandler getExceptionHandler() { return exceptionHandler; } public boolean isPostback() { RenderKit renderKit = getRenderKit(); if (renderKit == null) { String renderKitId = getApplication().getViewHandler().calculateRenderKitId(this); renderKit = JsfUtils.findFactory(RenderKitFactory.class).getRenderKit(this, renderKitId); } return renderKit.getResponseStateManager().isPostback(this); } public boolean isReleased() { return released; } public boolean isValidationFailed() { assertNotReleased(); return validationFailed; } public void setCurrentPhaseId(PhaseId currentPhaseId) { assertNotReleased(); this.currentPhaseId = currentPhaseId; } public void setExceptionHandler(ExceptionHandler exceptionHandler) { assertNotReleased(); this.exceptionHandler = exceptionHandler; } public void validationFailed() { assertNotReleased(); validationFailed = true; } public PartialViewContext getPartialViewContext() { assertNotReleased(); if (partialViewContext == null) { partialViewContext = JsfUtils.findFactory(PartialViewContextFactory.class).getPartialViewContext(this); } return partialViewContext; } public boolean isProcessingEvents() { assertNotReleased(); return processingEvents; } public void setProcessingEvents(boolean processingEvents) { assertNotReleased(); this.processingEvents = processingEvents; } public boolean isProjectStage(ProjectStage stage) { Assert.notNull(stage, "Stage must not be null"); return (stage.equals(getApplication().getProjectStage())); } private void assertNotReleased() { Assert.isTrue(!released, "FacesContext already released"); } private class PortletELContextImpl extends ELContext { private FunctionMapper functionMapper; private VariableMapper variableMapper; private ELResolver resolver; public PortletELContextImpl(ELResolver resolver) { this.resolver = resolver; } @Override public FunctionMapper getFunctionMapper() { return functionMapper; } @Override public VariableMapper getVariableMapper() { return variableMapper; } @Override public ELResolver getELResolver() { return resolver; } } private static class Message { private String clientId; private FacesMessage facesMessage; public Message(String clientId, FacesMessage facesMessage) { this.clientId = clientId; this.facesMessage = facesMessage; } public String getClientId() { return clientId; } public boolean isForClientId(String clientId) { return ObjectUtils.nullSafeEquals(this.clientId, clientId); } public FacesMessage getFacesMessage() { return facesMessage; } } }