1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.lifecycle;
21
22 import org.apache.myfaces.tobago.config.TobagoConfig;
23 import org.apache.myfaces.tobago.internal.component.AbstractUIPage;
24 import org.apache.myfaces.tobago.portlet.PortletUtils;
25 import org.apache.myfaces.tobago.renderkit.TobagoResponseStateManager;
26 import org.apache.myfaces.tobago.util.ComponentUtils;
27 import org.apache.myfaces.tobago.webapp.Secret;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import javax.faces.FacesException;
32 import javax.faces.application.Application;
33 import javax.faces.application.FacesMessage;
34 import javax.faces.application.ViewHandler;
35 import javax.faces.component.UIComponent;
36 import javax.faces.component.UIInput;
37 import javax.faces.component.UIViewRoot;
38 import javax.faces.context.ExternalContext;
39 import javax.faces.context.FacesContext;
40 import javax.faces.el.ValueBinding;
41 import javax.faces.event.PhaseId;
42 import java.io.IOException;
43 import java.lang.reflect.Method;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47
48
49
50
51
52
53 class RestoreViewExecutor implements PhaseExecutor {
54
55 private static final Logger LOG = LoggerFactory.getLogger(RestoreViewExecutor.class);
56
57 public boolean execute(FacesContext facesContext) {
58 ExternalContext externalContext = facesContext.getExternalContext();
59
60 Map sessionMap = externalContext.getSessionMap();
61 UIViewRoot viewRoot = (UIViewRoot) sessionMap.get(TobagoLifecycle.VIEW_ROOT_KEY);
62 if (viewRoot != null) {
63 facesContext.setViewRoot(viewRoot);
64 sessionMap.remove(TobagoLifecycle.VIEW_ROOT_KEY);
65
66 List<Object[]> messageHolders = (List<Object[]>) sessionMap.get(TobagoLifecycle.FACES_MESSAGES_KEY);
67 if (messageHolders != null) {
68 for (Object[] messageHolder : messageHolders) {
69 facesContext.addMessage((String) messageHolder[0], (FacesMessage) messageHolder[1]);
70 }
71 }
72 sessionMap.remove(TobagoLifecycle.FACES_MESSAGES_KEY);
73 if (viewRoot.getChildCount() > 0 && viewRoot.getChildren().get(0) instanceof AbstractUIPage) {
74 viewRoot.getChildren().get(0).decode(facesContext);
75 }
76 facesContext.renderResponse();
77 return true;
78 }
79
80 if (facesContext.getViewRoot() != null) {
81 facesContext.getViewRoot().setLocale(facesContext.getExternalContext().getRequestLocale());
82 ComponentUtils.resetPage(facesContext);
83 recursivelyHandleComponentReferencesAndSetValid(facesContext, facesContext.getViewRoot());
84 return false;
85 }
86
87
88 String viewId = deriveViewId(facesContext);
89
90 if (viewId == null) {
91
92 if (externalContext.getRequestServletPath() == null) {
93 return true;
94 }
95
96 if (!externalContext.getRequestServletPath().endsWith("/")) {
97 try {
98 externalContext.redirect(externalContext.getRequestServletPath() + "/");
99 facesContext.responseComplete();
100 return true;
101 } catch (IOException e) {
102 throw new FacesException("redirect failed", e);
103 }
104 }
105 }
106
107 Application application = facesContext.getApplication();
108 ViewHandler viewHandler = application.getViewHandler();
109
110 boolean postBack = isPostBack(facesContext);
111 if (postBack) {
112 viewRoot = viewHandler.restoreView(facesContext, viewId);
113 }
114 if (viewRoot == null) {
115 viewRoot = viewHandler.createView(facesContext, viewId);
116 viewRoot.setViewId(viewId);
117 facesContext.renderResponse();
118 }
119
120 facesContext.setViewRoot(viewRoot);
121 ComponentUtils.resetPage(facesContext);
122
123 if (!postBack) {
124
125 facesContext.renderResponse();
126 }
127
128 if (!isSessionSecretValid(facesContext)) {
129 if (LOG.isDebugEnabled()) {
130 LOG.debug("Secret is invalid!");
131 }
132 facesContext.renderResponse();
133 }
134
135 recursivelyHandleComponentReferencesAndSetValid(facesContext, viewRoot);
136
137 facesContext.getExternalContext().getRequestMap().put(TobagoLifecycle.VIEW_ROOT_KEY, viewRoot);
138 return false;
139 }
140
141 private boolean isPostBack(FacesContext facesContext) {
142 Map requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
143 return requestParameterMap.containsKey(TobagoResponseStateManager.VIEW_STATE_PARAM);
144 }
145
146 private boolean isSessionSecretValid(FacesContext facesContext) {
147 if (TobagoConfig.getInstance(FacesContext.getCurrentInstance()).isCheckSessionSecret()) {
148 return Secret.check(facesContext);
149 } else {
150 return true;
151 }
152 }
153
154 public PhaseId getPhase() {
155 return PhaseId.RESTORE_VIEW;
156 }
157
158 private static String deriveViewId(FacesContext facesContext) {
159 ExternalContext externalContext = facesContext.getExternalContext();
160
161 if (PortletUtils.isPortletRequest(facesContext)) {
162 return PortletUtils.getViewId(facesContext);
163 }
164
165 String viewId = externalContext.getRequestPathInfo();
166 if (viewId == null) {
167
168 viewId = externalContext.getRequestServletPath();
169 if (viewId == null) {
170 String msg = "RequestServletPath is null, cannot determine viewId of current page.";
171 LOG.error(msg);
172 throw new FacesException(msg);
173 }
174
175
176
177 String defaultSuffix = externalContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
178 String suffix = defaultSuffix != null ? defaultSuffix : ViewHandler.DEFAULT_SUFFIX;
179 if (suffix.charAt(0) != '.') {
180 String msg = "Default suffix must start with a dot!";
181 LOG.error(msg);
182 throw new FacesException(msg);
183 }
184
185 int dot = viewId.lastIndexOf('.');
186 if (dot == -1) {
187 LOG.error("Assumed extension mapping, but there is no extension in " + viewId);
188 viewId = null;
189 } else {
190 viewId = viewId.substring(0, dot) + suffix;
191 }
192 }
193
194 return viewId;
195 }
196
197
198
199 public static void recursivelyHandleComponentReferencesAndSetValid(FacesContext facesContext,
200 UIComponent parent) {
201 boolean forceHandle = false;
202
203 Method handleBindingsMethod = getBindingMethod(parent);
204
205 if (handleBindingsMethod != null && !forceHandle) {
206 try {
207 handleBindingsMethod.invoke(parent, new Object[]{});
208 } catch (Throwable th) {
209 LOG.error("Exception while invoking handleBindings on component with client-id:"
210 + parent.getClientId(facesContext), th);
211 }
212 } else {
213 for (Iterator it = parent.getFacetsAndChildren(); it.hasNext();) {
214 UIComponent component = (UIComponent) it.next();
215
216 ValueBinding binding = component.getValueBinding("binding");
217 if (binding != null && !binding.isReadOnly(facesContext)) {
218 binding.setValue(facesContext, component);
219 }
220
221 if (component instanceof UIInput) {
222 ((UIInput) component).setValid(true);
223 }
224
225 recursivelyHandleComponentReferencesAndSetValid(facesContext, component);
226 }
227 }
228 }
229
230
231
232
233
234
235 private static Method getBindingMethod(UIComponent parent) {
236 Class[] interfaces = parent.getClass().getInterfaces();
237
238 for (Class clazz : interfaces) {
239 if (clazz.getName().contains("BindingAware")) {
240 try {
241 return parent.getClass().getMethod("handleBindings", new Class[]{});
242 } catch (NoSuchMethodException e) {
243
244 }
245 }
246 }
247
248 return null;
249 }
250
251 }