| /* |
| * $Id$ |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.struts2.jsf; |
| |
| import java.io.IOException; |
| import java.lang.reflect.Method; |
| import java.util.Iterator; |
| |
| import javax.faces.FacesException; |
| import javax.faces.application.Application; |
| import javax.faces.application.ViewHandler; |
| import javax.faces.component.UIComponent; |
| import javax.faces.component.UIInput; |
| import javax.faces.component.UIViewRoot; |
| import javax.faces.context.ExternalContext; |
| import javax.faces.context.FacesContext; |
| import javax.faces.el.ValueBinding; |
| import javax.faces.event.PhaseId; |
| |
| /** |
| * Restores the view or component tree |
| */ |
| public class RestoreViewInterceptor extends FacesInterceptor { |
| |
| private static final long serialVersionUID = -1500785113037140668L; |
| |
| /** |
| * Restore View (JSF.2.2.1) |
| * |
| * @param viewId |
| * The view id |
| * @param facesContext |
| * The faces context |
| * @return true, if immediate rendering should occur |
| */ |
| protected boolean executePhase(String viewId, FacesContext facesContext) { |
| boolean skipFurtherProcessing = false; |
| if (log.isTraceEnabled()) |
| log.trace("entering restoreView"); |
| |
| informPhaseListenersBefore(facesContext, PhaseId.RESTORE_VIEW); |
| |
| try { |
| if (isResponseComplete(facesContext, "restoreView", true)) { |
| // have to skips this phase |
| return true; |
| } |
| if (shouldRenderResponse(facesContext, "restoreView", true)) { |
| skipFurtherProcessing = true; |
| } |
| |
| ExternalContext externalContext = facesContext.getExternalContext(); |
| String defaultSuffix = externalContext |
| .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME); |
| String suffix = defaultSuffix != null ? defaultSuffix |
| : ViewHandler.DEFAULT_SUFFIX; |
| if (viewId != null) { |
| viewId += suffix; |
| } |
| |
| if (viewId == null) { |
| if (!externalContext.getRequestServletPath().endsWith("/")) { |
| try { |
| externalContext.redirect(externalContext |
| .getRequestServletPath() |
| + "/"); |
| facesContext.responseComplete(); |
| return true; |
| } catch (IOException e) { |
| throw new FacesException("redirect failed", e); |
| } |
| } |
| } |
| |
| Application application = facesContext.getApplication(); |
| ViewHandler viewHandler = application.getViewHandler(); |
| |
| // boolean viewCreated = false; |
| UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId); |
| if (viewRoot == null) { |
| viewRoot = viewHandler.createView(facesContext, viewId); |
| viewRoot.setViewId(viewId); |
| facesContext.renderResponse(); |
| // viewCreated = true; |
| } |
| |
| facesContext.setViewRoot(viewRoot); |
| |
| /* |
| * This section has been disabled because it causes some bug. Be |
| * careful if you need to re-enable it. Furthermore, for an unknown |
| * reason, it seems that by default it is executed (i.e. |
| * log.isTraceEnabled() is true). Bug example : This traceView |
| * causes DebugUtil.printComponent to print all the attributes of |
| * the view components. And if you have a data table within an |
| * aliasBean, this causes the data table to initialize it's value |
| * attribute while the alias isn't set. So, the value initializes |
| * with an UIData.EMPTY_DATA_MODEL, and not with the aliased one. |
| * But as it's initialized, it will not try to get the value from |
| * the ValueBinding next time it needs to. I expect this to cause |
| * more similar bugs. TODO : Completely remove or be SURE by default |
| * it's not executed, and it has no more side-effects. |
| * |
| * if (log.isTraceEnabled()) { //Note: DebugUtils Logger must also |
| * be in trace level DebugUtils.traceView(viewCreated ? "Newly |
| * created view" : "Restored view"); } |
| */ |
| |
| if (facesContext.getExternalContext().getRequestParameterMap() |
| .isEmpty()) { |
| // no POST or query parameters --> set render response flag |
| facesContext.renderResponse(); |
| } |
| |
| recursivelyHandleComponentReferencesAndSetValid(facesContext, |
| viewRoot); |
| } finally { |
| informPhaseListenersAfter(facesContext, PhaseId.RESTORE_VIEW); |
| } |
| |
| if (isResponseComplete(facesContext, "restoreView", false) |
| || shouldRenderResponse(facesContext, "restoreView", false)) { |
| // since this phase is completed we don't need to return right away |
| // even if the response is completed |
| skipFurtherProcessing = true; |
| } |
| |
| if (!skipFurtherProcessing && log.isTraceEnabled()) |
| log.trace("exiting restoreView "); |
| return skipFurtherProcessing; |
| } |
| |
| /** |
| * Walk the component tree, executing any component-bindings to reattach |
| * components to their backing beans. Also, any UIInput component is marked |
| * as Valid. |
| * <p> |
| * Note that this method effectively breaks encapsulation; instead of asking |
| * each component to update itself and its children, this method just |
| * reaches into each component. That makes it impossible for any component |
| * to customise its behaviour at this point. |
| * <p> |
| * This has been filed as an issue against the spec. Until this issue is |
| * resolved, we'll add a new marker-interface for components to allow them |
| * to define their interest in handling children bindings themselves. |
| */ |
| protected void recursivelyHandleComponentReferencesAndSetValid( |
| FacesContext facesContext, UIComponent parent) { |
| recursivelyHandleComponentReferencesAndSetValid(facesContext, parent, |
| false); |
| } |
| |
| protected void recursivelyHandleComponentReferencesAndSetValid( |
| FacesContext facesContext, UIComponent parent, boolean forceHandle) { |
| Method handleBindingsMethod = getBindingMethod(parent); |
| |
| if (handleBindingsMethod != null && !forceHandle) { |
| try { |
| handleBindingsMethod.invoke(parent, new Object[] {}); |
| } catch (Throwable th) { |
| log.error( |
| "Exception while invoking handleBindings on component with client-id:" |
| + parent.getClientId(facesContext), th); |
| } |
| } else { |
| for (Iterator it = parent.getFacetsAndChildren(); it.hasNext();) { |
| UIComponent component = (UIComponent) it.next(); |
| |
| ValueBinding binding = component.getValueBinding("binding"); // TODO: |
| // constant |
| if (binding != null && !binding.isReadOnly(facesContext)) { |
| binding.setValue(facesContext, component); |
| } |
| |
| if (component instanceof UIInput) { |
| ((UIInput) component).setValid(true); |
| } |
| |
| recursivelyHandleComponentReferencesAndSetValid(facesContext, |
| component); |
| } |
| } |
| } |
| |
| /** |
| * This is all a hack to work around a spec-bug which will be fixed in |
| * JSF2.0 |
| * |
| * @param parent |
| * @return true if this component is bindingAware (e.g. aliasBean) |
| */ |
| private static Method getBindingMethod(UIComponent parent) { |
| Class[] clazzes = parent.getClass().getInterfaces(); |
| |
| for (int i = 0; i < clazzes.length; i++) { |
| Class clazz = clazzes[i]; |
| |
| if (clazz.getName().indexOf("BindingAware") != -1) { |
| try { |
| return parent.getClass().getMethod("handleBindings", |
| new Class[] {}); |
| } catch (NoSuchMethodException e) { |
| // return |
| } |
| } |
| } |
| |
| return null; |
| } |
| } |