1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package org.apache.myfaces.renderkit.html; |
20 | |
|
21 | |
import java.io.IOException; |
22 | |
import java.util.Map; |
23 | |
import java.util.logging.Logger; |
24 | |
|
25 | |
import javax.faces.context.ExternalContext; |
26 | |
import javax.faces.context.FacesContext; |
27 | |
import javax.faces.context.ResponseWriter; |
28 | |
import javax.faces.lifecycle.ClientWindow; |
29 | |
import javax.faces.render.RenderKitFactory; |
30 | |
import javax.faces.render.ResponseStateManager; |
31 | |
|
32 | |
import org.apache.myfaces.application.StateCache; |
33 | |
import org.apache.myfaces.application.StateCacheFactory; |
34 | |
import org.apache.myfaces.application.viewstate.StateCacheFactoryImpl; |
35 | |
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam; |
36 | |
import org.apache.myfaces.renderkit.MyfacesResponseStateManager; |
37 | |
import org.apache.myfaces.renderkit.StateTokenProcessor; |
38 | |
import org.apache.myfaces.shared.config.MyfacesConfig; |
39 | |
import org.apache.myfaces.shared.renderkit.html.HTML; |
40 | |
import org.apache.myfaces.shared.util.StateUtils; |
41 | |
import org.apache.myfaces.shared.util.WebConfigParamUtils; |
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
public class HtmlResponseStateManager extends MyfacesResponseStateManager |
48 | |
{ |
49 | 0 | private static final Logger log = Logger.getLogger(HtmlResponseStateManager.class.getName()); |
50 | |
|
51 | |
public static final String STANDARD_STATE_SAVING_PARAM = "javax.faces.ViewState"; |
52 | |
|
53 | |
private static final String VIEW_STATE_COUNTER = "oam.partial.VIEW_STATE_COUNTER"; |
54 | |
private static final String CLIENT_WINDOW_COUNTER = "oam.partial.CLIENT_WINDOW_COUNTER"; |
55 | |
|
56 | |
private static final String SESSION_TOKEN = "oam.rsm.SESSION_TOKEN"; |
57 | |
|
58 | |
|
59 | |
|
60 | |
|
61 | |
|
62 | |
|
63 | |
|
64 | |
|
65 | |
|
66 | |
|
67 | |
|
68 | |
|
69 | |
@Deprecated |
70 | |
@JSFWebConfigParam(since="2.0.6", expectedValues="true, false", defaultValue="true", group="state", |
71 | |
deprecated=true) |
72 | |
public static final String INIT_PARAM_HANDLE_STATE_CACHING_MECHANICS |
73 | |
= "org.apache.myfaces.HANDLE_STATE_CACHING_MECHANICS"; |
74 | |
|
75 | |
|
76 | |
|
77 | |
|
78 | |
@JSFWebConfigParam(since="2.2.8, 2.1.18, 2.0.24", expectedValues="true, false", |
79 | |
defaultValue="true", group="state") |
80 | |
public static final String INIT_PARAM_AUTOCOMPLETE_OFF_VIEW_STATE = |
81 | |
"org.apache.myfaces.AUTOCOMPLETE_OFF_VIEW_STATE"; |
82 | |
|
83 | |
private StateCacheFactory _stateCacheFactory; |
84 | |
|
85 | |
private StateTokenProcessor _stateTokenProcessor; |
86 | |
|
87 | |
private Boolean _autoCompleteOffViewState; |
88 | |
|
89 | |
public HtmlResponseStateManager() |
90 | 0 | { |
91 | 0 | _stateCacheFactory = new StateCacheFactoryImpl(); |
92 | 0 | _stateTokenProcessor = new DefaultStateTokenProcessor(); |
93 | 0 | _autoCompleteOffViewState = null; |
94 | 0 | } |
95 | |
|
96 | |
@Override |
97 | |
public void writeState(FacesContext facesContext, Object state) throws IOException |
98 | |
{ |
99 | 0 | ResponseWriter responseWriter = facesContext.getResponseWriter(); |
100 | |
|
101 | 0 | Object savedStateObject = null; |
102 | |
|
103 | 0 | if (!facesContext.getViewRoot().isTransient()) |
104 | |
{ |
105 | |
|
106 | 0 | savedStateObject = getStateCache(facesContext).encodeSerializedState(facesContext, state); |
107 | |
} |
108 | |
|
109 | |
|
110 | 0 | writeViewStateField(facesContext, responseWriter, savedStateObject); |
111 | |
|
112 | |
|
113 | 0 | writeRenderKitIdField(facesContext, responseWriter); |
114 | |
|
115 | |
|
116 | 0 | writeWindowIdField(facesContext, responseWriter); |
117 | 0 | } |
118 | |
|
119 | |
private void writeWindowIdField(FacesContext facesContext, ResponseWriter responseWriter) throws IOException |
120 | |
{ |
121 | 0 | ClientWindow clientWindow = facesContext.getExternalContext().getClientWindow(); |
122 | 0 | if (clientWindow != null) |
123 | |
{ |
124 | 0 | responseWriter.startElement(HTML.INPUT_ELEM, null); |
125 | 0 | responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null); |
126 | 0 | responseWriter.writeAttribute(HTML.ID_ATTR, generateUpdateClientWindowId(facesContext), null); |
127 | 0 | responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.CLIENT_WINDOW_PARAM, null); |
128 | 0 | responseWriter.writeAttribute(HTML.VALUE_ATTR, clientWindow.getId(), null); |
129 | 0 | responseWriter.endElement(HTML.INPUT_ELEM); |
130 | |
} |
131 | 0 | } |
132 | |
|
133 | |
@Override |
134 | |
public void saveState(FacesContext facesContext, Object state) |
135 | |
{ |
136 | 0 | if (!facesContext.getViewRoot().isTransient()) |
137 | |
{ |
138 | 0 | getStateCache(facesContext).saveSerializedView(facesContext, state); |
139 | |
} |
140 | 0 | } |
141 | |
|
142 | |
private void writeViewStateField(FacesContext facesContext, ResponseWriter responseWriter, Object savedState) |
143 | |
throws IOException |
144 | |
{ |
145 | 0 | String serializedState = _stateTokenProcessor.encode(facesContext, savedState); |
146 | 0 | ExternalContext extContext = facesContext.getExternalContext(); |
147 | 0 | MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(extContext); |
148 | |
|
149 | 0 | responseWriter.startElement(HTML.INPUT_ELEM, null); |
150 | 0 | responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null); |
151 | 0 | responseWriter.writeAttribute(HTML.NAME_ATTR, STANDARD_STATE_SAVING_PARAM, null); |
152 | 0 | if (myfacesConfig.isRenderViewStateId()) |
153 | |
{ |
154 | |
|
155 | |
|
156 | |
|
157 | 0 | responseWriter.writeAttribute(HTML.ID_ATTR, |
158 | |
HtmlResponseStateManager.generateUpdateViewStateId( |
159 | |
facesContext), null); |
160 | |
} |
161 | 0 | responseWriter.writeAttribute(HTML.VALUE_ATTR, serializedState, null); |
162 | 0 | if (this.isAutocompleteOffViewState(facesContext)) |
163 | |
{ |
164 | 0 | responseWriter.writeAttribute(HTML.AUTOCOMPLETE_ATTR, "off", null); |
165 | |
} |
166 | 0 | responseWriter.endElement(HTML.INPUT_ELEM); |
167 | 0 | } |
168 | |
|
169 | |
private void writeRenderKitIdField(FacesContext facesContext, ResponseWriter responseWriter) throws IOException |
170 | |
{ |
171 | |
|
172 | 0 | String defaultRenderKitId = facesContext.getApplication().getDefaultRenderKitId(); |
173 | 0 | if (defaultRenderKitId != null && !RenderKitFactory.HTML_BASIC_RENDER_KIT.equals(defaultRenderKitId)) |
174 | |
{ |
175 | 0 | responseWriter.startElement(HTML.INPUT_ELEM, null); |
176 | 0 | responseWriter.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null); |
177 | 0 | responseWriter.writeAttribute(HTML.NAME_ATTR, ResponseStateManager.RENDER_KIT_ID_PARAM, null); |
178 | 0 | responseWriter.writeAttribute(HTML.VALUE_ATTR, defaultRenderKitId, null); |
179 | 0 | responseWriter.endElement(HTML.INPUT_ELEM); |
180 | |
} |
181 | 0 | } |
182 | |
|
183 | |
@Override |
184 | |
public Object getState(FacesContext facesContext, String viewId) |
185 | |
{ |
186 | 0 | Object savedState = getSavedState(facesContext); |
187 | 0 | if (savedState == null) |
188 | |
{ |
189 | 0 | return null; |
190 | |
} |
191 | |
|
192 | 0 | return getStateCache(facesContext).restoreSerializedView(facesContext, viewId, savedState); |
193 | |
} |
194 | |
|
195 | |
|
196 | |
|
197 | |
|
198 | |
|
199 | |
|
200 | |
|
201 | |
|
202 | |
|
203 | |
private Object getSavedState(FacesContext facesContext) |
204 | |
{ |
205 | 0 | Object encodedState = |
206 | |
facesContext.getExternalContext().getRequestParameterMap().get(STANDARD_STATE_SAVING_PARAM); |
207 | 0 | if(encodedState==null || (((String) encodedState).length() == 0)) |
208 | |
{ |
209 | 0 | return null; |
210 | |
} |
211 | |
|
212 | 0 | Object savedStateObject = _stateTokenProcessor.decode(facesContext, (String)encodedState); |
213 | |
|
214 | 0 | return savedStateObject; |
215 | |
} |
216 | |
|
217 | |
|
218 | |
|
219 | |
|
220 | |
|
221 | |
|
222 | |
@Override |
223 | |
public boolean isPostback(FacesContext context) |
224 | |
{ |
225 | 0 | return context.getExternalContext().getRequestParameterMap().containsKey(ResponseStateManager.VIEW_STATE_PARAM); |
226 | |
} |
227 | |
|
228 | |
@Override |
229 | |
public String getViewState(FacesContext facesContext, Object baseState) |
230 | |
{ |
231 | |
|
232 | |
|
233 | |
|
234 | |
|
235 | 0 | if (baseState == null) |
236 | |
{ |
237 | 0 | return null; |
238 | |
} |
239 | 0 | if (facesContext.getViewRoot().isTransient()) |
240 | |
{ |
241 | 0 | return null; |
242 | |
} |
243 | |
|
244 | 0 | Object state = getStateCache(facesContext).saveSerializedView(facesContext, baseState); |
245 | |
|
246 | 0 | return _stateTokenProcessor.encode(facesContext, state); |
247 | |
} |
248 | |
|
249 | |
@Override |
250 | |
public boolean isStateless(FacesContext context, String viewId) |
251 | |
{ |
252 | 0 | if (context.isPostback()) |
253 | |
{ |
254 | 0 | String encodedState = |
255 | |
context.getExternalContext().getRequestParameterMap().get(STANDARD_STATE_SAVING_PARAM); |
256 | 0 | if(encodedState==null || (((String) encodedState).length() == 0)) |
257 | |
{ |
258 | 0 | return false; |
259 | |
} |
260 | |
|
261 | 0 | return _stateTokenProcessor.isStateless(context, encodedState); |
262 | |
} |
263 | |
else |
264 | |
{ |
265 | |
|
266 | |
|
267 | |
|
268 | 0 | throw new IllegalStateException( |
269 | |
"Cannot decide if the view is stateless or not, since the request is " |
270 | |
+ "not postback (no preceding writeState(...))."); |
271 | |
} |
272 | |
} |
273 | |
|
274 | |
@Override |
275 | |
public String getCryptographicallyStrongTokenFromSession(FacesContext context) |
276 | |
{ |
277 | 0 | Map<String, Object> sessionMap = context.getExternalContext().getSessionMap(); |
278 | 0 | String savedToken = (String) sessionMap.get(SESSION_TOKEN); |
279 | 0 | if (savedToken == null) |
280 | |
{ |
281 | 0 | savedToken = getStateCache(context).createCryptographicallyStrongTokenFromSession(context); |
282 | 0 | sessionMap.put(SESSION_TOKEN, savedToken); |
283 | |
} |
284 | 0 | return savedToken; |
285 | |
} |
286 | |
|
287 | |
@Override |
288 | |
public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext) |
289 | |
{ |
290 | 0 | return getStateCache(facesContext).isWriteStateAfterRenderViewRequired(facesContext); |
291 | |
} |
292 | |
|
293 | |
protected StateCache getStateCache(FacesContext facesContext) |
294 | |
{ |
295 | 0 | return _stateCacheFactory.getStateCache(facesContext); |
296 | |
} |
297 | |
|
298 | |
public static String generateUpdateClientWindowId(FacesContext facesContext) |
299 | |
{ |
300 | |
|
301 | |
|
302 | |
|
303 | |
|
304 | |
|
305 | |
|
306 | |
|
307 | |
|
308 | |
|
309 | |
|
310 | |
|
311 | |
|
312 | |
String id; |
313 | 0 | char separator = facesContext.getNamingContainerSeparatorChar(); |
314 | 0 | Integer count = (Integer) facesContext.getAttributes().get(CLIENT_WINDOW_COUNTER); |
315 | 0 | if (count == null) |
316 | |
{ |
317 | 0 | count = Integer.valueOf(0); |
318 | |
} |
319 | 0 | count += 1; |
320 | 0 | id = facesContext.getViewRoot().getContainerClientId(facesContext) + |
321 | |
separator + ResponseStateManager.CLIENT_WINDOW_PARAM + separator + count; |
322 | 0 | facesContext.getAttributes().put(CLIENT_WINDOW_COUNTER, count); |
323 | 0 | return id; |
324 | |
} |
325 | |
|
326 | |
public static String generateUpdateViewStateId(FacesContext facesContext) |
327 | |
{ |
328 | |
|
329 | |
|
330 | |
|
331 | |
|
332 | |
|
333 | |
|
334 | |
|
335 | |
|
336 | |
|
337 | |
|
338 | |
|
339 | |
|
340 | |
String id; |
341 | 0 | char separator = facesContext.getNamingContainerSeparatorChar(); |
342 | 0 | Integer count = (Integer) facesContext.getAttributes().get(VIEW_STATE_COUNTER); |
343 | 0 | if (count == null) |
344 | |
{ |
345 | 0 | count = Integer.valueOf(0); |
346 | |
} |
347 | 0 | count += 1; |
348 | 0 | id = facesContext.getViewRoot().getContainerClientId(facesContext) + |
349 | |
separator + ResponseStateManager.VIEW_STATE_PARAM + separator + count; |
350 | 0 | facesContext.getAttributes().put(VIEW_STATE_COUNTER, count); |
351 | 0 | return id; |
352 | |
} |
353 | |
|
354 | 0 | private static class DefaultStateTokenProcessor extends StateTokenProcessor |
355 | |
{ |
356 | |
private static final String STATELESS_TOKEN = "stateless"; |
357 | |
|
358 | |
@Override |
359 | |
public Object decode(FacesContext facesContext, String token) |
360 | |
{ |
361 | 0 | if (STATELESS_TOKEN.equals(token)) |
362 | |
{ |
363 | |
|
364 | |
|
365 | 0 | return null; |
366 | |
} |
367 | 0 | Object savedStateObject = StateUtils.reconstruct((String)token, facesContext.getExternalContext()); |
368 | 0 | return savedStateObject; |
369 | |
} |
370 | |
|
371 | |
@Override |
372 | |
public String encode(FacesContext facesContext, Object savedStateObject) |
373 | |
{ |
374 | 0 | if (facesContext.getViewRoot().isTransient()) |
375 | |
{ |
376 | 0 | return STATELESS_TOKEN; |
377 | |
} |
378 | 0 | String serializedState = StateUtils.construct(savedStateObject, facesContext.getExternalContext()); |
379 | 0 | return serializedState; |
380 | |
} |
381 | |
|
382 | |
@Override |
383 | |
public boolean isStateless(FacesContext facesContext, String token) |
384 | |
{ |
385 | 0 | return STATELESS_TOKEN.equals(token); |
386 | |
} |
387 | |
} |
388 | |
|
389 | |
private boolean isAutocompleteOffViewState(FacesContext facesContext) |
390 | |
{ |
391 | 0 | if (_autoCompleteOffViewState == null) |
392 | |
{ |
393 | 0 | _autoCompleteOffViewState = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(), |
394 | |
INIT_PARAM_AUTOCOMPLETE_OFF_VIEW_STATE, true); |
395 | |
} |
396 | 0 | return _autoCompleteOffViewState; |
397 | |
} |
398 | |
} |