1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.jsf;
19
20 import java.io.IOException;
21 import java.lang.reflect.Method;
22 import java.util.Iterator;
23
24 import javax.faces.FacesException;
25 import javax.faces.application.Application;
26 import javax.faces.application.ViewHandler;
27 import javax.faces.component.UIComponent;
28 import javax.faces.component.UIInput;
29 import javax.faces.component.UIViewRoot;
30 import javax.faces.context.ExternalContext;
31 import javax.faces.context.FacesContext;
32 import javax.faces.el.ValueBinding;
33 import javax.faces.event.PhaseId;
34
35 /***
36 * Restores the view or component tree
37 */
38 public class RestoreViewInterceptor extends FacesInterceptor {
39
40 private static final long serialVersionUID = -1500785113037140668L;
41
42 /***
43 * Restore View (JSF.2.2.1)
44 *
45 * @param viewId
46 * The view id
47 * @param facesContext
48 * The faces context
49 * @return true, if immediate rendering should occur
50 */
51 protected boolean executePhase(String viewId, FacesContext facesContext) {
52 boolean skipFurtherProcessing = false;
53 if (log.isTraceEnabled())
54 log.trace("entering restoreView");
55
56 informPhaseListenersBefore(facesContext, PhaseId.RESTORE_VIEW);
57
58 try {
59 if (isResponseComplete(facesContext, "restoreView", true)) {
60
61 return true;
62 }
63 if (shouldRenderResponse(facesContext, "restoreView", true)) {
64 skipFurtherProcessing = true;
65 }
66
67 ExternalContext externalContext = facesContext.getExternalContext();
68 String defaultSuffix = externalContext
69 .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
70 String suffix = defaultSuffix != null ? defaultSuffix
71 : ViewHandler.DEFAULT_SUFFIX;
72 if (viewId != null) {
73 viewId += suffix;
74 }
75
76 if (viewId == null) {
77 if (!externalContext.getRequestServletPath().endsWith("/")) {
78 try {
79 externalContext.redirect(externalContext
80 .getRequestServletPath()
81 + "/");
82 facesContext.responseComplete();
83 return true;
84 } catch (IOException e) {
85 throw new FacesException("redirect failed", e);
86 }
87 }
88 }
89
90 Application application = facesContext.getApplication();
91 ViewHandler viewHandler = application.getViewHandler();
92
93
94 UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId);
95 if (viewRoot == null) {
96 viewRoot = viewHandler.createView(facesContext, viewId);
97 viewRoot.setViewId(viewId);
98 facesContext.renderResponse();
99
100 }
101
102 facesContext.setViewRoot(viewRoot);
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 if (facesContext.getExternalContext().getRequestParameterMap()
125 .isEmpty()) {
126
127 facesContext.renderResponse();
128 }
129
130 recursivelyHandleComponentReferencesAndSetValid(facesContext,
131 viewRoot);
132 } finally {
133 informPhaseListenersAfter(facesContext, PhaseId.RESTORE_VIEW);
134 }
135
136 if (isResponseComplete(facesContext, "restoreView", false)
137 || shouldRenderResponse(facesContext, "restoreView", false)) {
138
139
140 skipFurtherProcessing = true;
141 }
142
143 if (!skipFurtherProcessing && log.isTraceEnabled())
144 log.trace("exiting restoreView ");
145 return skipFurtherProcessing;
146 }
147
148 /***
149 * Walk the component tree, executing any component-bindings to reattach
150 * components to their backing beans. Also, any UIInput component is marked
151 * as Valid.
152 * <p>
153 * Note that this method effectively breaks encapsulation; instead of asking
154 * each component to update itself and its children, this method just
155 * reaches into each component. That makes it impossible for any component
156 * to customise its behaviour at this point.
157 * <p>
158 * This has been filed as an issue against the spec. Until this issue is
159 * resolved, we'll add a new marker-interface for components to allow them
160 * to define their interest in handling children bindings themselves.
161 */
162 protected void recursivelyHandleComponentReferencesAndSetValid(
163 FacesContext facesContext, UIComponent parent) {
164 recursivelyHandleComponentReferencesAndSetValid(facesContext, parent,
165 false);
166 }
167
168 protected void recursivelyHandleComponentReferencesAndSetValid(
169 FacesContext facesContext, UIComponent parent, boolean forceHandle) {
170 Method handleBindingsMethod = getBindingMethod(parent);
171
172 if (handleBindingsMethod != null && !forceHandle) {
173 try {
174 handleBindingsMethod.invoke(parent, new Object[] {});
175 } catch (Throwable th) {
176 log.error(
177 "Exception while invoking handleBindings on component with client-id:"
178 + parent.getClientId(facesContext), th);
179 }
180 } else {
181 for (Iterator it = parent.getFacetsAndChildren(); it.hasNext();) {
182 UIComponent component = (UIComponent) it.next();
183
184 ValueBinding binding = component.getValueBinding("binding");
185
186 if (binding != null && !binding.isReadOnly(facesContext)) {
187 binding.setValue(facesContext, component);
188 }
189
190 if (component instanceof UIInput) {
191 ((UIInput) component).setValid(true);
192 }
193
194 recursivelyHandleComponentReferencesAndSetValid(facesContext,
195 component);
196 }
197 }
198 }
199
200 /***
201 * This is all a hack to work around a spec-bug which will be fixed in
202 * JSF2.0
203 *
204 * @param parent
205 * @return true if this component is bindingAware (e.g. aliasBean)
206 */
207 private static Method getBindingMethod(UIComponent parent) {
208 Class[] clazzes = parent.getClass().getInterfaces();
209
210 for (int i = 0; i < clazzes.length; i++) {
211 Class clazz = clazzes[i];
212
213 if (clazz.getName().indexOf("BindingAware") != -1) {
214 try {
215 return parent.getClass().getMethod("handleBindings",
216 new Class[] {});
217 } catch (NoSuchMethodException e) {
218
219 }
220 }
221 }
222
223 return null;
224 }
225 }