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((String) 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 ObjectOutputStream out = new ObjectOutputStream(os);
410
411 out.writeObject(serializedView);
412 out.close();
413 baos.close();
414
415 if (log.isLoggable(Level.FINEST))
416 {
417 log.finest("Exiting serializeView - serialized. Bytes : " + baos.size());
418 }
419 return baos.toByteArray();
420 }
421 catch (IOException e)
422 {
423 log.log(Level.SEVERE, "Exiting serializeView - Could not serialize state: " + e.getMessage(), e);
424 return null;
425 }
426 }
427
428
429 if (log.isLoggable(Level.FINEST))
430 {
431 log.finest("Exiting serializeView - do not serialize state in session.");
432 }
433
434 return serializedView;
435
436 }
437
438
439
440
441
442
443
444 protected boolean isSerializeStateInSession(FacesContext context)
445 {
446 String value = context.getExternalContext().getInitParameter(
447 SERIALIZE_STATE_IN_SESSION_PARAM);
448 boolean serialize = DEFAULT_SERIALIZE_STATE_IN_SESSION;
449 if (value != null)
450 {
451 serialize = Boolean.valueOf(value);
452 }
453 return serialize;
454 }
455
456
457
458
459
460
461
462 protected boolean isCompressStateInSession(FacesContext context)
463 {
464 String value = context.getExternalContext().getInitParameter(
465 COMPRESS_SERVER_STATE_PARAM);
466 boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
467 if (value != null)
468 {
469 compress = Boolean.valueOf(value);
470 }
471 return compress;
472 }
473
474 protected Object deserializeView(Object state)
475 {
476 if (log.isLoggable(Level.FINEST))
477 {
478 log.finest("Entering deserializeView");
479 }
480
481 if(state instanceof byte[])
482 {
483 if (log.isLoggable(Level.FINEST))
484 {
485 log.finest("Processing deserializeView - deserializing serialized state. Bytes : "
486 + ((byte[]) state).length);
487 }
488
489 try
490 {
491 ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
492 InputStream is = bais;
493 if(is.read() == COMPRESSED_FLAG)
494 {
495 is = new GZIPInputStream(is);
496 }
497 ObjectInputStream ois = null;
498 try
499 {
500 final ObjectInputStream in = new MyFacesObjectInputStream(is);
501 ois = in;
502 Object object = null;
503 if (System.getSecurityManager() != null)
504 {
505 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
506 {
507 public Object run() throws PrivilegedActionException, IOException, ClassNotFoundException
508 {
509
510 return in.readObject();
511 }
512 });
513 }
514 else
515 {
516
517 object = in.readObject();
518 }
519 return object;
520 }
521 finally
522 {
523 if (ois != null)
524 {
525 ois.close();
526 ois = null;
527 }
528 }
529 }
530 catch (PrivilegedActionException e)
531 {
532 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
533 return null;
534 }
535 catch (IOException e)
536 {
537 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
538 return null;
539 }
540 catch (ClassNotFoundException e)
541 {
542 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
543 return null;
544 }
545 }
546 else if (state instanceof Object[])
547 {
548 if (log.isLoggable(Level.FINEST))
549 {
550 log.finest("Exiting deserializeView - state not serialized.");
551 }
552
553 return state;
554 }
555 else if(state == null)
556 {
557 log.severe("Exiting deserializeView - this method should not be called with a null-state.");
558 return null;
559 }
560 else
561 {
562 log.severe("Exiting deserializeView - this method should not be called with a state of type : "
563 + state.getClass());
564 return null;
565 }
566 }
567
568
569
570 @Override
571 public Object saveSerializedView(FacesContext facesContext, Object serializedView)
572 {
573 if (log.isLoggable(Level.FINEST))
574 {
575 log.finest("Processing saveSerializedView - server-side state saving - save state");
576 }
577
578 saveSerializedViewInServletSession(facesContext, serializedView);
579
580 if (log.isLoggable(Level.FINEST))
581 {
582 log.finest("Exiting saveSerializedView - server-side state saving - saved state");
583 }
584
585 return encodeSerializedState(facesContext, serializedView);
586 }
587
588 @Override
589 public Object restoreSerializedView(FacesContext facesContext, String viewId, Object viewState)
590 {
591 if (log.isLoggable(Level.FINEST))
592 {
593 log.finest("Restoring view from session");
594 }
595
596 Object serverStateId = getServerStateId(facesContext, viewState);
597
598 return (serverStateId == null)
599 ? null
600 : getSerializedViewFromServletSession(facesContext, viewId, serverStateId);
601 }
602
603 public Object encodeSerializedState(FacesContext facesContext, Object serializedView)
604 {
605 return getKeyFactory(facesContext).encode(getNextViewSequence(facesContext));
606 }
607
608 @Override
609 public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
610 {
611 return false;
612 }
613
614
615
616 private boolean isUseFlashScopePurgeViewsInSession(ExternalContext externalContext)
617 {
618 if (_useFlashScopePurgeViewsInSession == null)
619 {
620 _useFlashScopePurgeViewsInSession = WebConfigParamUtils.getBooleanInitParameter(
621 externalContext, USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION, false);
622 }
623 return _useFlashScopePurgeViewsInSession;
624 }
625
626 private Integer getNumberOfSequentialViewsInSession(ExternalContext externalContext)
627 {
628 if (!_numberOfSequentialViewsInSessionSet)
629 {
630 _numberOfSequentialViewsInSession = WebConfigParamUtils.getIntegerInitParameter(
631 externalContext,
632 NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
633 _numberOfSequentialViewsInSessionSet = true;
634 }
635 return _numberOfSequentialViewsInSession;
636 }
637
638 protected KeyFactory getKeyFactory(FacesContext facesContext)
639 {
640
641 return sessionViewStorageFactory.getKeyFactory();
642 }
643
644 protected SessionViewStorageFactory getSessionViewStorageFactory()
645 {
646 return sessionViewStorageFactory;
647 }
648 }