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