1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.application.viewstate;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.ObjectInputStream;
26 import java.io.ObjectOutputStream;
27 import java.io.OutputStream;
28 import java.security.AccessController;
29 import java.security.PrivilegedActionException;
30 import java.security.PrivilegedExceptionAction;
31 import java.util.Map;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34 import java.util.zip.GZIPInputStream;
35 import java.util.zip.GZIPOutputStream;
36
37 import javax.faces.context.ExternalContext;
38 import javax.faces.context.FacesContext;
39 import org.apache.myfaces.application.StateCache;
40 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
41 import org.apache.myfaces.shared.renderkit.RendererUtils;
42 import org.apache.myfaces.shared.util.MyFacesObjectInputStream;
43 import org.apache.myfaces.shared.util.WebConfigParamUtils;
44
45 class ServerSideStateCacheImpl extends StateCache<Object, Object>
46 {
47 private static final Logger log = Logger.getLogger(ServerSideStateCacheImpl.class.getName());
48
49 public static final String SERIALIZED_VIEW_SESSION_ATTR=
50 ServerSideStateCacheImpl.class.getName() + ".SERIALIZED_VIEW";
51
52 public static final String RESTORED_SERIALIZED_VIEW_REQUEST_ATTR =
53 ServerSideStateCacheImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW";
54
55 public static final String RESTORED_VIEW_KEY_REQUEST_ATTR =
56 ServerSideStateCacheImpl.class.getName() + ".RESTORED_VIEW_KEY";
57
58
59
60
61
62
63
64
65 @JSFWebConfigParam(defaultValue="20",since="1.1", classType="java.lang.Integer", group="state", tags="performance")
66 public static final String NUMBER_OF_VIEWS_IN_SESSION_PARAM = "org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION";
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 @JSFWebConfigParam(since="2.0.6", classType="java.lang.Integer", group="state", tags="performance")
82 public static final String NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM
83 = "org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION";
84
85
86
87
88 public static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 20;
89
90
91
92
93
94
95
96
97
98 @JSFWebConfigParam(defaultValue="true",since="1.1", expectedValues="true,false", group="state", tags="performance")
99 public static final String SERIALIZE_STATE_IN_SESSION_PARAM = "org.apache.myfaces.SERIALIZE_STATE_IN_SESSION";
100
101
102
103
104
105
106
107
108
109 @JSFWebConfigParam(defaultValue="true",since="1.1", expectedValues="true,false", group="state", tags="performance")
110 public static final String COMPRESS_SERVER_STATE_PARAM = "org.apache.myfaces.COMPRESS_STATE_IN_SESSION";
111
112
113
114
115 public static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;
116
117
118
119
120 public static final boolean DEFAULT_SERIALIZE_STATE_IN_SESSION = true;
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 @JSFWebConfigParam(defaultValue="off", expectedValues="off, no, hard-soft, soft, soft-weak, weak",
146 since="1.2.5", group="state", tags="performance")
147 public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE = "org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE";
148
149
150
151
152
153
154 public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT = "hard-soft";
155
156 public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT = "soft";
157
158 public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK = "soft-weak";
159
160 public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK = "weak";
161
162 public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF = "off";
163
164
165
166
167
168
169
170
171
172 @JSFWebConfigParam(since="2.0.6", defaultValue="false", expectedValues="true, false", group="state")
173 public static final String USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION
174 = "org.apache.myfaces.USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION";
175
176 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_NONE = "none";
177 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM = "secureRandom";
178 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM = "random";
179
180
181
182
183 @JSFWebConfigParam(since="2.1.9, 2.0.15", expectedValues="secureRandom, random, none",
184 defaultValue="none", group="state")
185 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM
186 = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN";
187 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT =
188 RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_NONE;
189
190
191
192
193
194 @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="8", group="state")
195 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM
196 = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH";
197 public static final int RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM_DEFAULT = 8;
198
199
200
201
202
203 @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
204 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS_PARAM
205 = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS";
206
207
208
209
210 @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
211 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER_PARAM
212 = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER";
213
214
215
216
217
218 @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="SHA1PRNG", group="state")
219 public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM_PARAM
220 = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM";
221
222
223 public static final int UNCOMPRESSED_FLAG = 0;
224 public static final int COMPRESSED_FLAG = 1;
225
226 private Boolean _useFlashScopePurgeViewsInSession = null;
227
228 private Integer _numberOfSequentialViewsInSession = null;
229 private boolean _numberOfSequentialViewsInSessionSet = false;
230
231 private SessionViewStorageFactory sessionViewStorageFactory;
232
233 public ServerSideStateCacheImpl()
234 {
235 FacesContext facesContext = FacesContext.getCurrentInstance();
236 String randomMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
237 RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM,
238 RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT);
239 if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM.equals(randomMode))
240 {
241 sessionViewStorageFactory = new RandomSessionViewStorageFactory(
242 new SecureRandomKeyFactory(facesContext));
243 }
244 else if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM.equals(randomMode))
245 {
246 sessionViewStorageFactory = new RandomSessionViewStorageFactory(
247 new RandomKeyFactory(facesContext));
248 }
249 else
250 {
251 sessionViewStorageFactory = new CounterSessionViewStorageFactory(new CounterKeyFactory());
252 }
253 }
254
255
256
257 protected Object getServerStateId(FacesContext facesContext, Object state)
258 {
259 if (state != null)
260 {
261 return getKeyFactory(facesContext).decode(state);
262 }
263 return null;
264 }
265
266 protected void saveSerializedViewInServletSession(FacesContext context,
267 Object serializedView)
268 {
269 Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
270 SerializedViewCollection viewCollection = (SerializedViewCollection) sessionMap
271 .get(SERIALIZED_VIEW_SESSION_ATTR);
272 if (viewCollection == null)
273 {
274 viewCollection = getSessionViewStorageFactory().createSerializedViewCollection(context);
275 sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
276 }
277
278 Map<Object,Object> attributeMap = context.getAttributes();
279
280 SerializedViewKey key = null;
281 if (getNumberOfSequentialViewsInSession(context.getExternalContext()) != null &&
282 getNumberOfSequentialViewsInSession(context.getExternalContext()) > 0)
283 {
284 key = (SerializedViewKey) attributeMap.get(RESTORED_VIEW_KEY_REQUEST_ATTR);
285
286 if (key == null )
287 {
288 if (isUseFlashScopePurgeViewsInSession(context.getExternalContext()) &&
289 Boolean.TRUE.equals(context.getExternalContext().getRequestMap()
290 .get("oam.Flash.REDIRECT.PREVIOUSREQUEST")))
291 {
292 key = (SerializedViewKey)
293 context.getExternalContext().getFlash().get(RESTORED_VIEW_KEY_REQUEST_ATTR);
294 }
295 }
296 }
297
298 SerializedViewKey nextKey = getSessionViewStorageFactory().createSerializedViewKey(
299 context, context.getViewRoot().getViewId(), getNextViewSequence(context));
300 viewCollection.add(context, serializeView(context, serializedView), nextKey, key);
301
302
303 sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
304 }
305
306 protected Object getSerializedViewFromServletSession(FacesContext context, String viewId, Object sequence)
307 {
308 ExternalContext externalContext = context.getExternalContext();
309 Map<Object, Object> attributeMap = context.getAttributes();
310 Object serializedView = null;
311 if (attributeMap.containsKey(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR))
312 {
313 serializedView = attributeMap.get(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR);
314 }
315 else
316 {
317 SerializedViewCollection viewCollection = (SerializedViewCollection) externalContext
318 .getSessionMap().get(SERIALIZED_VIEW_SESSION_ATTR);
319 if (viewCollection != null)
320 {
321 if (sequence != null)
322 {
323 Object state = viewCollection.get(
324 getSessionViewStorageFactory().createSerializedViewKey(
325 context, viewId, sequence));
326 if (state != null)
327 {
328 serializedView = deserializeView(state);
329 }
330 }
331 }
332 attributeMap.put(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
333
334 if (getNumberOfSequentialViewsInSession(externalContext) != null &&
335 getNumberOfSequentialViewsInSession(externalContext) > 0)
336 {
337 SerializedViewKey key = getSessionViewStorageFactory().
338 createSerializedViewKey(context, viewId, sequence);
339 attributeMap.put(RESTORED_VIEW_KEY_REQUEST_ATTR, key);
340
341 if (isUseFlashScopePurgeViewsInSession(externalContext))
342 {
343 externalContext.getFlash().put(RESTORED_VIEW_KEY_REQUEST_ATTR, key);
344 externalContext.getFlash().keep(RESTORED_VIEW_KEY_REQUEST_ATTR);
345 }
346 }
347
348 nextViewSequence(context);
349 }
350 return serializedView;
351 }
352
353 public Object getNextViewSequence(FacesContext context)
354 {
355 Object sequence = context.getAttributes().get(RendererUtils.SEQUENCE_PARAM);
356 if (sequence == null)
357 {
358 sequence = nextViewSequence(context);
359 context.getAttributes().put(RendererUtils.SEQUENCE_PARAM, sequence);
360 }
361 return sequence;
362 }
363
364 public Object nextViewSequence(FacesContext facescontext)
365 {
366 Object sequence = getKeyFactory(facescontext).generateKey(facescontext);
367 facescontext.getAttributes().put(RendererUtils.SEQUENCE_PARAM, sequence);
368 return sequence;
369 }
370
371 protected Object serializeView(FacesContext context, Object serializedView)
372 {
373 if (log.isLoggable(Level.FINEST))
374 {
375 log.finest("Entering serializeView");
376 }
377
378 if(isSerializeStateInSession(context))
379 {
380 if (log.isLoggable(Level.FINEST))
381 {
382 log.finest("Processing serializeView - serialize state in session");
383 }
384
385 ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
386 try
387 {
388 OutputStream os = baos;
389 if(isCompressStateInSession(context))
390 {
391 if (log.isLoggable(Level.FINEST))
392 {
393 log.finest("Processing serializeView - serialize compressed");
394 }
395
396 os.write(COMPRESSED_FLAG);
397 os = new GZIPOutputStream(os, 1024);
398 }
399 else
400 {
401 if (log.isLoggable(Level.FINEST))
402 {
403 log.finest("Processing serializeView - serialize uncompressed");
404 }
405
406 os.write(UNCOMPRESSED_FLAG);
407 }
408
409
410
411 ObjectOutputStream out = new ObjectOutputStream(os);
412
413 out.writeObject(serializedView);
414
415
416 out.close();
417 baos.close();
418
419 if (log.isLoggable(Level.FINEST))
420 {
421 log.finest("Exiting serializeView - serialized. Bytes : " + baos.size());
422 }
423 return baos.toByteArray();
424 }
425 catch (IOException e)
426 {
427 log.log(Level.SEVERE, "Exiting serializeView - Could not serialize state: " + e.getMessage(), e);
428 return null;
429 }
430 }
431
432
433 if (log.isLoggable(Level.FINEST))
434 {
435 log.finest("Exiting serializeView - do not serialize state in session.");
436 }
437
438 return serializedView;
439
440 }
441
442
443
444
445
446
447
448 protected boolean isSerializeStateInSession(FacesContext context)
449 {
450 String value = context.getExternalContext().getInitParameter(
451 SERIALIZE_STATE_IN_SESSION_PARAM);
452 boolean serialize = DEFAULT_SERIALIZE_STATE_IN_SESSION;
453 if (value != null)
454 {
455 serialize = Boolean.valueOf(value);
456 }
457 return serialize;
458 }
459
460
461
462
463
464
465
466 protected boolean isCompressStateInSession(FacesContext context)
467 {
468 String value = context.getExternalContext().getInitParameter(
469 COMPRESS_SERVER_STATE_PARAM);
470 boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
471 if (value != null)
472 {
473 compress = Boolean.valueOf(value);
474 }
475 return compress;
476 }
477
478 protected Object deserializeView(Object state)
479 {
480 if (log.isLoggable(Level.FINEST))
481 {
482 log.finest("Entering deserializeView");
483 }
484
485 if(state instanceof byte[])
486 {
487 if (log.isLoggable(Level.FINEST))
488 {
489 log.finest("Processing deserializeView - deserializing serialized state. Bytes : "
490 + ((byte[]) state).length);
491 }
492
493 try
494 {
495 ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
496 InputStream is = bais;
497 if(is.read() == COMPRESSED_FLAG)
498 {
499 is = new GZIPInputStream(is);
500 }
501 ObjectInputStream ois = null;
502 try
503 {
504 final ObjectInputStream in = new MyFacesObjectInputStream(is);
505 ois = in;
506 Object object = null;
507 if (System.getSecurityManager() != null)
508 {
509 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
510 {
511 public Object run() throws PrivilegedActionException, IOException, ClassNotFoundException
512 {
513
514 return in.readObject();
515 }
516 });
517 }
518 else
519 {
520
521 object = in.readObject();
522 }
523 return object;
524 }
525 finally
526 {
527 if (ois != null)
528 {
529 ois.close();
530 ois = null;
531 }
532 }
533 }
534 catch (PrivilegedActionException e)
535 {
536 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
537 return null;
538 }
539 catch (IOException e)
540 {
541 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
542 return null;
543 }
544 catch (ClassNotFoundException e)
545 {
546 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
547 return null;
548 }
549 }
550 else if (state instanceof Object[])
551 {
552 if (log.isLoggable(Level.FINEST))
553 {
554 log.finest("Exiting deserializeView - state not serialized.");
555 }
556
557 return state;
558 }
559 else if(state == null)
560 {
561 log.severe("Exiting deserializeView - this method should not be called with a null-state.");
562 return null;
563 }
564 else
565 {
566 log.severe("Exiting deserializeView - this method should not be called with a state of type : "
567 + state.getClass());
568 return null;
569 }
570 }
571
572
573
574 @Override
575 public Object saveSerializedView(FacesContext facesContext, Object serializedView)
576 {
577 if (log.isLoggable(Level.FINEST))
578 {
579 log.finest("Processing saveSerializedView - server-side state saving - save state");
580 }
581
582 saveSerializedViewInServletSession(facesContext, serializedView);
583
584 if (log.isLoggable(Level.FINEST))
585 {
586 log.finest("Exiting saveSerializedView - server-side state saving - saved state");
587 }
588
589 return encodeSerializedState(facesContext, serializedView);
590 }
591
592 @Override
593 public Object restoreSerializedView(FacesContext facesContext, String viewId, Object viewState)
594 {
595 if (log.isLoggable(Level.FINEST))
596 {
597 log.finest("Restoring view from session");
598 }
599
600 Object serverStateId = getServerStateId(facesContext, viewState);
601
602 return (serverStateId == null)
603 ? null
604 : getSerializedViewFromServletSession(facesContext, viewId, serverStateId);
605 }
606
607 public Object encodeSerializedState(FacesContext facesContext, Object serializedView)
608 {
609 return getKeyFactory(facesContext).encode(getNextViewSequence(facesContext));
610 }
611
612 @Override
613 public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
614 {
615 return false;
616 }
617
618
619
620 private boolean isUseFlashScopePurgeViewsInSession(ExternalContext externalContext)
621 {
622 if (_useFlashScopePurgeViewsInSession == null)
623 {
624 _useFlashScopePurgeViewsInSession = WebConfigParamUtils.getBooleanInitParameter(
625 externalContext, USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION, false);
626 }
627 return _useFlashScopePurgeViewsInSession;
628 }
629
630 private Integer getNumberOfSequentialViewsInSession(ExternalContext externalContext)
631 {
632 if (!_numberOfSequentialViewsInSessionSet)
633 {
634 _numberOfSequentialViewsInSession = WebConfigParamUtils.getIntegerInitParameter(
635 externalContext,
636 NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
637 _numberOfSequentialViewsInSessionSet = true;
638 }
639 return _numberOfSequentialViewsInSession;
640 }
641
642 protected KeyFactory getKeyFactory(FacesContext facesContext)
643 {
644
645 return sessionViewStorageFactory.getKeyFactory();
646 }
647
648 protected SessionViewStorageFactory getSessionViewStorageFactory()
649 {
650 return sessionViewStorageFactory;
651 }
652 }