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