1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.trinidad.bean.util;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.IOException;
23 import java.io.ObjectOutputStream;
24 import java.io.Serializable;
25
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.concurrent.ConcurrentMap;
34
35 import javax.faces.component.StateHolder;
36 import javax.faces.context.ExternalContext;
37 import javax.faces.context.FacesContext;
38
39 import org.apache.myfaces.trinidad.bean.FacesBean;
40 import org.apache.myfaces.trinidad.bean.PropertyKey;
41 import org.apache.myfaces.trinidad.bean.PropertyMap;
42 import org.apache.myfaces.trinidad.context.RequestContext;
43 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
44
45
46
47
48
49 public final class StateUtils
50 {
51 static
52 {
53
54 boolean checkPropertyStateSerialization = false;
55 boolean checkComponentStateSerialization = false;
56 boolean checkComponentTreeStateSerialization = false;
57 boolean checkSessionSerialization = false;
58 boolean checkApplicationSerialization = false;
59 boolean checkMangedBeanMutation = false;
60 boolean checkAtEndRequest = false;
61
62 String checkSerializationProperty;
63
64
65
66 try
67 {
68 checkSerializationProperty =
69 System.getProperty("org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION");
70 }
71 catch (Throwable t)
72 {
73 checkSerializationProperty = null;
74 }
75
76 if (checkSerializationProperty != null)
77 {
78 checkSerializationProperty = checkSerializationProperty.toUpperCase();
79
80
81 String[] paramArray = checkSerializationProperty.split(",");
82
83 Set<String> serializationFlags = new HashSet<String>(Arrays.asList(paramArray));
84
85 if (!serializationFlags.contains("NONE"))
86 {
87 if (serializationFlags.contains("ALL"))
88 {
89 checkPropertyStateSerialization = true;
90 checkComponentStateSerialization = true;
91 checkComponentTreeStateSerialization = true;
92 checkSessionSerialization = true;
93 checkApplicationSerialization = true;
94 checkMangedBeanMutation = true;
95 checkAtEndRequest = true;
96 }
97 else
98 {
99 checkPropertyStateSerialization = serializationFlags.contains("PROPERTY");
100 checkComponentStateSerialization = serializationFlags.contains("COMPONENT");
101 checkComponentTreeStateSerialization = serializationFlags.contains("TREE");
102 checkSessionSerialization = serializationFlags.contains("SESSION");
103 checkApplicationSerialization = serializationFlags.contains("APPLICATION");
104 checkMangedBeanMutation = serializationFlags.contains("BEANS");
105 checkAtEndRequest = serializationFlags.contains("REQUEST");
106 }
107 }
108 }
109
110 _CHECK_PROPERTY_STATE_SERIALIZATION = checkPropertyStateSerialization;
111 _CHECK_COMPONENT_STATE_SERIALIZATION = checkComponentStateSerialization;
112 _CHECK_COMPONENT_TREE_STATE_SERIALIZATION = checkComponentTreeStateSerialization;
113 _CHECK_SESSION_SERIALIZATION = checkSessionSerialization;
114 _CHECK_APPLICATION_SERIALIZATION = checkApplicationSerialization;
115 _CHECK_MANAGED_BEAN_MUTATATION = checkMangedBeanMutation;
116 _CHECK_AT_END_REQUEST = checkAtEndRequest;
117 }
118
119 private static final boolean _CHECK_COMPONENT_TREE_STATE_SERIALIZATION;
120 private static final boolean _CHECK_COMPONENT_STATE_SERIALIZATION;
121 private static final boolean _CHECK_PROPERTY_STATE_SERIALIZATION;
122 private static final boolean _CHECK_SESSION_SERIALIZATION;
123 private static final boolean _CHECK_APPLICATION_SERIALIZATION;
124 private static final boolean _CHECK_MANAGED_BEAN_MUTATATION;
125 private static final boolean _CHECK_AT_END_REQUEST;
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144 private static boolean _checkPropertyStateSerialization()
145 {
146 return _CHECK_PROPERTY_STATE_SERIALIZATION;
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public static boolean checkComponentStateSerialization(FacesContext context)
168 {
169 return _CHECK_COMPONENT_STATE_SERIALIZATION;
170 }
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 public static boolean checkComponentTreeStateSerialization(FacesContext context)
192 {
193 return _CHECK_COMPONENT_TREE_STATE_SERIALIZATION;
194 }
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 public static boolean checkSessionSerialization(ExternalContext extContext)
215 {
216 return _CHECK_SESSION_SERIALIZATION;
217 }
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237 public static boolean checkApplicationSerialization(ExternalContext extContext)
238 {
239 return _CHECK_APPLICATION_SERIALIZATION;
240 }
241
242
243
244
245
246
247
248
249
250
251
252 public static boolean checkManagedBeanMutation(ExternalContext extContext)
253 {
254 return _CHECK_MANAGED_BEAN_MUTATATION;
255 }
256
257
258
259
260
261
262
263 public static boolean checkScopesAtEndOfRequest(ExternalContext extContext)
264 {
265 return _CHECK_AT_END_REQUEST;
266 }
267
268
269
270
271 static public Object saveKey(PropertyKey key)
272 {
273 int index = key.getIndex();
274 if (index < 0)
275 return key.getName();
276
277 if (index < 128)
278 return Byte.valueOf((byte) index);
279
280 return Integer.valueOf(index);
281 }
282
283
284
285
286
287 static public PropertyKey restoreKey(FacesBean.Type type, Object value)
288 {
289 PropertyKey key;
290 if (value instanceof Number)
291 {
292 key = type.findKey(((Number) value).intValue());
293 if (key == null)
294 throw new IllegalStateException(_LOG.getMessage(
295 "INVALID_INDEX"));
296 }
297 else
298 {
299 key = type.findKey((String) value);
300 if (key == null)
301 key = PropertyKey.createPropertyKey((String) value);
302 }
303
304 return key;
305 }
306
307
308
309
310 static public Object saveState(
311 PropertyMap map,
312 FacesContext context,
313 boolean useStateHolder)
314 {
315 int size = map.size();
316 if (size == 0)
317 return null;
318
319 Object[] values = new Object[2 * size];
320 int i = 0;
321 for(Map.Entry<PropertyKey, Object> entry : map.entrySet())
322 {
323 PropertyKey key = entry.getKey();
324 if (key.isTransient())
325 {
326
327
328
329 entry.setValue(null);
330 continue;
331 }
332
333 Object value = entry.getValue();
334
335 values[i] = saveKey(key);
336 if (_LOG.isFinest())
337 {
338 _LOG.finest("SAVE {" + key + "=" + value + "}");
339 }
340
341 Object saveValue;
342
343 if (useStateHolder)
344 saveValue = saveStateHolder(context, value);
345 else
346 saveValue = key.saveValue(context, value);
347
348
349 if (_checkPropertyStateSerialization())
350 {
351 try
352 {
353 new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(saveValue);
354 }
355 catch (IOException e)
356 {
357 throw new RuntimeException(_LOG.getMessage("UNSERIALIZABLE_PROPERTY_VALUE",
358 new Object[]{saveValue, key, map}),
359 e);
360 }
361 }
362
363 values[i + 1] = saveValue;
364
365 i+=2;
366 }
367
368 return values;
369 }
370
371
372
373
374
375 static public void restoreState(
376 PropertyMap map,
377 FacesContext context,
378 FacesBean.Type type,
379 Object state,
380 boolean useStateHolder)
381 {
382 if (state == null)
383 return;
384
385 Object[] values = (Object[]) state;
386 int size = values.length / 2;
387 for (int i = 0; i < size; i++)
388 {
389 Object savedKey = values[i * 2];
390 if (savedKey == null)
391 continue;
392
393 Object savedValue = values[i * 2 + 1];
394 PropertyKey key = restoreKey(type, savedKey);
395 Object value;
396
397 if (useStateHolder)
398 value = restoreStateHolder(context, savedValue);
399 else
400 value = key.restoreValue(context, savedValue);
401
402 if (_LOG.isFinest())
403 {
404 _LOG.finest("RESTORE {" + key + "=" + value + "}");
405 }
406
407 map.put(key, value);
408 }
409 }
410
411
412
413
414 static public Object saveStateHolder(
415 FacesContext context,
416 Object value)
417 {
418 if (value == null)
419 return null;
420
421 Saver saver = null;
422 if (value instanceof StateHolder)
423 {
424 if (((StateHolder) value).isTransient())
425 return null;
426
427 saver = new SHSaver();
428 }
429 else if (value instanceof Serializable)
430 {
431 return value;
432 }
433 else
434 {
435 saver = new Saver();
436 }
437
438 if (saver != null)
439 saver.saveState(context, value);
440
441 return saver;
442 }
443
444
445
446
447 static public Object restoreStateHolder(
448 FacesContext context,
449 Object savedValue)
450 {
451 if (!(savedValue instanceof Saver))
452 return savedValue;
453
454 return ((Saver) savedValue).restoreState(context);
455 }
456
457
458
459
460
461
462 @SuppressWarnings("unchecked")
463 static public Object saveList(
464 FacesContext context,
465 Object value)
466 {
467 if (value == null)
468 return null;
469
470 List<Object> list = (List<Object>) value;
471 int size = list.size();
472 if (size == 0)
473 return null;
474
475 Object[] array = new Object[size];
476
477
478
479
480
481
482 int index = 0;
483 for(Object object : list)
484 {
485 array[index++] = saveStateHolder(context, object);
486 }
487
488 return array;
489 }
490
491
492
493
494 static public Object restoreList(
495 FacesContext context,
496 Object savedValue)
497 {
498 if (savedValue == null)
499 return null;
500
501 Object[] array = (Object[]) savedValue;
502 int length = array.length;
503 if (length == 0)
504 return null;
505
506 List<Object> list = new ArrayList<Object>(length);
507 for(Object state : array)
508 {
509 Object restored = restoreStateHolder(context, state);
510 if (restored != null)
511 {
512 list.add(restored);
513 }
514 }
515
516 return list;
517 }
518
519
520
521
522
523
524
525 static private class Saver implements Serializable
526 {
527 public void saveState(FacesContext context, Object saved)
528 {
529 _name = saved.getClass().getName();
530 }
531
532 public Object restoreState(FacesContext context)
533 {
534
535
536
537 ConcurrentMap<String, Object> appMap =
538 RequestContext.getCurrentInstance().getApplicationScopedConcurrentMap();
539
540
541 Map<String, Class> classMap = (Map<String, Class>) appMap.get(_CLASS_MAP_KEY);
542
543 if (classMap == null)
544 {
545
546
547 Map<String, Class> newClassMap = new HashMap<String, Class>();
548 Map<String, Class> oldClassMap =
549 (Map<String, Class>) appMap.putIfAbsent(_CLASS_MAP_KEY, newClassMap);
550
551 if (oldClassMap != null)
552 classMap = oldClassMap;
553 else
554 classMap = newClassMap;
555 }
556
557 Class clazz = classMap.get(_name);
558
559 if (clazz == null)
560 {
561 try
562 {
563 ClassLoader cl = _getClassLoader();
564 clazz = cl.loadClass(_name);
565 classMap.put(_name, clazz);
566 }
567 catch (Throwable t)
568 {
569 _LOG.severe(t);
570 return null;
571 }
572 }
573
574 try
575 {
576 return clazz.newInstance();
577 }
578 catch (Throwable t)
579 {
580 _LOG.severe(t);
581 return null;
582 }
583 }
584
585 private String _name;
586 private static final long serialVersionUID = 1L;
587 }
588
589
590
591
592
593 static private class SHSaver extends Saver
594 {
595 @Override
596 public void saveState(FacesContext context, Object value)
597 {
598 super.saveState(context, value);
599 _save = ((StateHolder) value).saveState(context);
600 }
601
602 @Override
603 public Object restoreState(FacesContext context)
604 {
605 Object o = super.restoreState(context);
606 if (o != null)
607 ((StateHolder) o).restoreState(context, _save);
608
609 return o;
610 }
611
612 private Object _save;
613 private static final long serialVersionUID = 1L;
614 }
615
616
617
618
619
620 static private ClassLoader _getClassLoader()
621 {
622 ClassLoader cl = Thread.currentThread().getContextClassLoader();
623 if (cl == null)
624 cl = StateUtils.class.getClassLoader();
625
626 return cl;
627 }
628
629 static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(StateUtils.class);
630
631 private static final String _CLASS_MAP_KEY =
632 "org.apache.myfaces.trinidad.bean.util.CLASS_MAP_KEY";
633
634
635 }