Source Code Cross Referenced for SerializableChecker.java in  » J2EE » wicket » org » apache » wicket » util » io » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » wicket » org.apache.wicket.util.io 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Licensed to the Apache Software Foundation (ASF) under one or more
003:         * contributor license agreements.  See the NOTICE file distributed with
004:         * this work for additional information regarding copyright ownership.
005:         * The ASF licenses this file to You under the Apache License, Version 2.0
006:         * (the "License"); you may not use this file except in compliance with
007:         * the License.  You may obtain a copy of the License at
008:         *
009:         *      http://www.apache.org/licenses/LICENSE-2.0
010:         *
011:         * Unless required by applicable law or agreed to in writing, software
012:         * distributed under the License is distributed on an "AS IS" BASIS,
013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014:         * See the License for the specific language governing permissions and
015:         * limitations under the License.
016:         */
017:        package org.apache.wicket.util.io;
018:
019:        import java.io.Externalizable;
020:        import java.io.IOException;
021:        import java.io.NotSerializableException;
022:        import java.io.ObjectOutput;
023:        import java.io.ObjectOutputStream;
024:        import java.io.ObjectStreamClass;
025:        import java.io.ObjectStreamField;
026:        import java.io.OutputStream;
027:        import java.io.Serializable;
028:        import java.lang.reflect.Field;
029:        import java.lang.reflect.InvocationTargetException;
030:        import java.lang.reflect.Method;
031:        import java.lang.reflect.Proxy;
032:        import java.util.Date;
033:        import java.util.HashMap;
034:        import java.util.IdentityHashMap;
035:        import java.util.Iterator;
036:        import java.util.LinkedList;
037:        import java.util.Map;
038:
039:        import org.apache.wicket.Component;
040:        import org.apache.wicket.WicketRuntimeException;
041:        import org.slf4j.Logger;
042:        import org.slf4j.LoggerFactory;
043:
044:        /**
045:         * Utility class that analyzes objects for non-serializable nodes. Construct
046:         * with the object you want to check, and then call {@link #check()}. When a
047:         * non-serializable object is found, a {@link WicketNotSerializableException} is
048:         * thrown with a message that shows the trace up to the not-serializable object.
049:         * The exception is thrown for the first non-serializable instance it
050:         * encounters, so multiple problems will not be shown.
051:         * <p>
052:         * As this class depends heavily on JDK's serialization internals using
053:         * introspection, analyzing may not be possible, for instance when the runtime
054:         * environment does not have sufficient rights to set fields accesible that
055:         * would otherwise be hidden. You should call
056:         * {@link SerializableChecker#isAvailable()} to see whether this class can
057:         * operate properly. If it doesn't, you should fall back to e.g. re-throwing/
058:         * printing the {@link NotSerializableException} you probably got before using
059:         * this class.
060:         * </p>
061:         * 
062:         * @author eelcohillenius
063:         * @author Al Maw
064:         */
065:        public final class SerializableChecker extends ObjectOutputStream {
066:            /**
067:             * Exception that is thrown when a non-serializable object was found.
068:             */
069:            public static final class WicketNotSerializableException extends
070:                    WicketRuntimeException {
071:                private static final long serialVersionUID = 1L;
072:
073:                WicketNotSerializableException(String message, Throwable cause) {
074:                    super (message, cause);
075:                }
076:            }
077:
078:            /**
079:             * Does absolutely nothing.
080:             */
081:            private static class NoopOutputStream extends OutputStream {
082:                public void close() {
083:                }
084:
085:                public void flush() {
086:                }
087:
088:                public void write(byte[] b) {
089:                }
090:
091:                public void write(byte[] b, int i, int l) {
092:                }
093:
094:                public void write(int b) {
095:                }
096:            }
097:
098:            private static abstract class ObjectOutputAdaptor implements 
099:                    ObjectOutput {
100:
101:                public void close() throws IOException {
102:                }
103:
104:                public void flush() throws IOException {
105:                }
106:
107:                public void write(byte[] b) throws IOException {
108:                }
109:
110:                public void write(byte[] b, int off, int len)
111:                        throws IOException {
112:                }
113:
114:                public void write(int b) throws IOException {
115:                }
116:
117:                public void writeBoolean(boolean v) throws IOException {
118:                }
119:
120:                public void writeByte(int v) throws IOException {
121:                }
122:
123:                public void writeBytes(String s) throws IOException {
124:                }
125:
126:                public void writeChar(int v) throws IOException {
127:                }
128:
129:                public void writeChars(String s) throws IOException {
130:                }
131:
132:                public void writeDouble(double v) throws IOException {
133:                }
134:
135:                public void writeFloat(float v) throws IOException {
136:                }
137:
138:                public void writeInt(int v) throws IOException {
139:                }
140:
141:                public void writeLong(long v) throws IOException {
142:                }
143:
144:                public void writeShort(int v) throws IOException {
145:                }
146:
147:                public void writeUTF(String str) throws IOException {
148:                }
149:            }
150:
151:            /** Holds information about the field and the resulting object being traced. */
152:            private static final class TraceSlot {
153:                private final String fieldDescription;
154:
155:                private final Object object;
156:
157:                TraceSlot(Object object, String fieldDescription) {
158:                    super ();
159:                    this .object = object;
160:                    this .fieldDescription = fieldDescription;
161:                }
162:
163:                public String toString() {
164:                    return object.getClass() + " - " + fieldDescription;
165:                }
166:            }
167:
168:            private static final NoopOutputStream DUMMY_OUTPUT_STREAM = new NoopOutputStream();
169:
170:            /** log. */
171:            private static final Logger log = LoggerFactory
172:                    .getLogger(SerializableChecker.class);
173:
174:            /** Whether we can execute the tests. If false, check will just return. */
175:            private static boolean available = true;
176:
177:            // this hack - accessing the serialization API through introspection - is
178:            // the only way to use Java serialization for our purposes without writing
179:            // the whole thing from scratch (and even then, it would be limited). This
180:            // way of working is of course fragile for internal API changes, but as we
181:            // do an extra check on availability and we report when we can't use this
182:            // introspection fu, we'll find out soon enough and clients on this class
183:            // can fall back on Java's default exception for serialization errors (which
184:            // sucks and is the main reason for this attempt).
185:            private static final Method LOOKUP_METHOD;
186:
187:            private static final Method GET_CLASS_DATA_LAYOUT_METHOD;
188:
189:            private static final Method GET_NUM_OBJ_FIELDS_METHOD;
190:
191:            private static final Method GET_OBJ_FIELD_VALUES_METHOD;
192:
193:            private static final Method GET_FIELD_METHOD;
194:
195:            private static final Method HAS_WRITE_REPLACE_METHOD_METHOD;
196:
197:            private static final Method INVOKE_WRITE_REPLACE_METHOD;
198:
199:            static {
200:                try {
201:                    LOOKUP_METHOD = ObjectStreamClass.class
202:                            .getDeclaredMethod("lookup", new Class[] {
203:                                    Class.class, Boolean.TYPE });
204:                    LOOKUP_METHOD.setAccessible(true);
205:
206:                    GET_CLASS_DATA_LAYOUT_METHOD = ObjectStreamClass.class
207:                            .getDeclaredMethod("getClassDataLayout", null);
208:                    GET_CLASS_DATA_LAYOUT_METHOD.setAccessible(true);
209:
210:                    GET_NUM_OBJ_FIELDS_METHOD = ObjectStreamClass.class
211:                            .getDeclaredMethod("getNumObjFields", null);
212:                    GET_NUM_OBJ_FIELDS_METHOD.setAccessible(true);
213:
214:                    GET_OBJ_FIELD_VALUES_METHOD = ObjectStreamClass.class
215:                            .getDeclaredMethod(
216:                                    "getObjFieldValues",
217:                                    new Class[] { Object.class, Object[].class });
218:                    GET_OBJ_FIELD_VALUES_METHOD.setAccessible(true);
219:
220:                    GET_FIELD_METHOD = ObjectStreamField.class
221:                            .getDeclaredMethod("getField", null);
222:                    GET_FIELD_METHOD.setAccessible(true);
223:
224:                    HAS_WRITE_REPLACE_METHOD_METHOD = ObjectStreamClass.class
225:                            .getDeclaredMethod("hasWriteReplaceMethod", null);
226:                    HAS_WRITE_REPLACE_METHOD_METHOD.setAccessible(true);
227:
228:                    INVOKE_WRITE_REPLACE_METHOD = ObjectStreamClass.class
229:                            .getDeclaredMethod("invokeWriteReplace",
230:                                    new Class[] { Object.class });
231:                    INVOKE_WRITE_REPLACE_METHOD.setAccessible(true);
232:                } catch (SecurityException e) {
233:                    available = false;
234:                    throw new RuntimeException(e);
235:                } catch (NoSuchMethodException e) {
236:                    available = false;
237:                    throw new RuntimeException(e);
238:                }
239:            }
240:
241:            /**
242:             * Gets whether we can execute the tests. If false, calling {@link #check()}
243:             * will just return and you are advised to rely on the
244:             * {@link NotSerializableException}. Clients are advised to call this
245:             * method prior to calling the check method.
246:             * 
247:             * @return whether security settings and underlying API etc allow for
248:             *         accessing the serialization API using introspection
249:             */
250:            public static boolean isAvailable() {
251:                return available;
252:            }
253:
254:            /** object stack that with the trace path. */
255:            private final LinkedList traceStack = new LinkedList();
256:
257:            /** set for checking circular references. */
258:            private final Map checked = new IdentityHashMap();
259:
260:            /** string stack with current names pushed. */
261:            private final LinkedList nameStack = new LinkedList();
262:
263:            /** root object being analyzed. */
264:            private Object root;
265:
266:            /** cache for classes - writeObject methods. */
267:            private final Map writeObjectMethodCache = new HashMap();
268:
269:            /** current simple field name. */
270:            private String simpleName = "";
271:
272:            /** current full field description. */
273:            private String fieldDescription;
274:
275:            /** Exception that should be set as the cause when throwing a new exception. */
276:            private final NotSerializableException exception;
277:
278:            /**
279:             * Construct.
280:             * 
281:             * @param exception
282:             *            exception that should be set as the cause when throwing a new
283:             *            exception
284:             * 
285:             * @throws IOException
286:             */
287:            public SerializableChecker(NotSerializableException exception)
288:                    throws IOException {
289:                this .exception = exception;
290:            }
291:
292:            /**
293:             * @see java.io.ObjectOutputStream#reset()
294:             */
295:            public void reset() throws IOException {
296:                root = null;
297:                checked.clear();
298:                fieldDescription = null;
299:                simpleName = null;
300:                traceStack.clear();
301:                nameStack.clear();
302:                writeObjectMethodCache.clear();
303:            }
304:
305:            private void check(Object obj) {
306:                if (obj == null) {
307:                    return;
308:                }
309:
310:                Class cls = obj.getClass();
311:                nameStack.add(simpleName);
312:                traceStack.add(new TraceSlot(obj, fieldDescription));
313:
314:                if (!(obj instanceof  Serializable)
315:                        && (!Proxy.isProxyClass(cls))) {
316:                    throw new WicketNotSerializableException(
317:                            toPrettyPrintedStack(obj.getClass().getName())
318:                                    .toString(), exception);
319:                }
320:
321:                ObjectStreamClass desc;
322:                for (;;) {
323:                    try {
324:                        desc = (ObjectStreamClass) LOOKUP_METHOD.invoke(null,
325:                                new Object[] { cls, Boolean.TRUE });
326:                        Class repCl;
327:                        if (!((Boolean) HAS_WRITE_REPLACE_METHOD_METHOD.invoke(
328:                                desc, null)).booleanValue()
329:                                || (obj = INVOKE_WRITE_REPLACE_METHOD.invoke(
330:                                        desc, new Object[] { obj })) == null
331:                                || (repCl = obj.getClass()) == cls) {
332:                            break;
333:                        }
334:                        cls = repCl;
335:                    } catch (IllegalAccessException e) {
336:                        throw new RuntimeException(e);
337:                    } catch (InvocationTargetException e) {
338:                        throw new RuntimeException(e);
339:                    }
340:                }
341:
342:                if (cls.isPrimitive()) {
343:                    // skip
344:                } else if (cls.isArray()) {
345:                    checked.put(obj, null);
346:                    Class ccl = cls.getComponentType();
347:                    if (!(ccl.isPrimitive())) {
348:                        Object[] objs = (Object[]) obj;
349:                        for (int i = 0; i < objs.length; i++) {
350:                            String arrayPos = "[" + i + "]";
351:                            simpleName = arrayPos;
352:                            fieldDescription += arrayPos;
353:                            check(objs[i]);
354:                        }
355:                    }
356:                } else if (obj instanceof  Externalizable
357:                        && (!Proxy.isProxyClass(cls))) {
358:                    Externalizable extObj = (Externalizable) obj;
359:                    try {
360:                        extObj.writeExternal(new ObjectOutputAdaptor() {
361:                            private int count = 0;
362:
363:                            public void writeObject(Object streamObj)
364:                                    throws IOException {
365:                                // Check for circular reference.
366:                                if (checked.containsKey(streamObj)) {
367:                                    return;
368:                                }
369:
370:                                checked.put(streamObj, null);
371:                                String arrayPos = "[write:" + count++ + "]";
372:                                simpleName = arrayPos;
373:                                fieldDescription += arrayPos;
374:
375:                                check(streamObj);
376:                            }
377:                        });
378:                    } catch (Exception e) {
379:                        if (e instanceof  WicketNotSerializableException) {
380:                            throw (WicketNotSerializableException) e;
381:                        }
382:                        log.warn("error delegating to Externalizable : "
383:                                + e.getMessage() + ", path: " + currentPath());
384:                    }
385:                } else {
386:                    Method writeObjectMethod = null;
387:                    Object o = writeObjectMethodCache.get(cls);
388:                    if (o != null) {
389:                        if (o instanceof  Method) {
390:                            writeObjectMethod = (Method) o;
391:                        }
392:                    } else {
393:                        try {
394:                            writeObjectMethod = cls
395:                                    .getDeclaredMethod(
396:                                            "writeObject",
397:                                            new Class[] { java.io.ObjectOutputStream.class });
398:                        } catch (SecurityException e) {
399:                            // we can't access/ set accessible to true
400:                            writeObjectMethodCache.put(cls, Boolean.FALSE);
401:                        } catch (NoSuchMethodException e) {
402:                            // cls doesn't have that method
403:                            writeObjectMethodCache.put(cls, Boolean.FALSE);
404:                        }
405:                    }
406:
407:                    final Object original = obj;
408:                    if (writeObjectMethod != null) {
409:                        class InterceptingObjectOutputStream extends
410:                                ObjectOutputStream {
411:                            private int counter;
412:
413:                            InterceptingObjectOutputStream() throws IOException {
414:                                super (DUMMY_OUTPUT_STREAM);
415:                                enableReplaceObject(true);
416:                            }
417:
418:                            protected Object replaceObject(Object streamObj)
419:                                    throws IOException {
420:                                if (streamObj == original) {
421:                                    return streamObj;
422:                                }
423:
424:                                counter++;
425:                                // Check for circular reference.
426:                                if (checked.containsKey(streamObj)) {
427:                                    return null;
428:                                }
429:
430:                                checked.put(original, null);
431:                                String arrayPos = "[write:" + counter + "]";
432:                                simpleName = arrayPos;
433:                                fieldDescription += arrayPos;
434:                                check(streamObj);
435:                                return streamObj;
436:                            }
437:                        }
438:                        try {
439:                            InterceptingObjectOutputStream ioos = new InterceptingObjectOutputStream();
440:                            ioos.writeObject(obj);
441:                        } catch (Exception e) {
442:                            if (e instanceof  WicketNotSerializableException) {
443:                                throw (WicketNotSerializableException) e;
444:                            }
445:                            log.warn("error delegating to writeObject : "
446:                                    + e.getMessage() + ", path: "
447:                                    + currentPath());
448:                        }
449:                    } else {
450:                        Object[] slots;
451:                        try {
452:                            slots = (Object[]) GET_CLASS_DATA_LAYOUT_METHOD
453:                                    .invoke(desc, null);
454:                        } catch (Exception e) {
455:                            throw new RuntimeException(e);
456:                        }
457:                        for (int i = 0; i < slots.length; i++) {
458:                            ObjectStreamClass slotDesc;
459:                            try {
460:                                Field descField = slots[i].getClass()
461:                                        .getDeclaredField("desc");
462:                                descField.setAccessible(true);
463:                                slotDesc = (ObjectStreamClass) descField
464:                                        .get(slots[i]);
465:                            } catch (Exception e) {
466:                                throw new RuntimeException(e);
467:                            }
468:                            checked.put(obj, null);
469:                            checkFields(obj, slotDesc);
470:                        }
471:                    }
472:                }
473:
474:                traceStack.removeLast();
475:                nameStack.removeLast();
476:            }
477:
478:            private void checkFields(Object obj, ObjectStreamClass desc) {
479:                int numFields;
480:                try {
481:                    numFields = ((Integer) GET_NUM_OBJ_FIELDS_METHOD.invoke(
482:                            desc, null)).intValue();
483:                } catch (IllegalAccessException e) {
484:                    throw new RuntimeException(e);
485:                } catch (InvocationTargetException e) {
486:                    throw new RuntimeException(e);
487:                }
488:
489:                if (numFields > 0) {
490:                    int numPrimFields;
491:                    ObjectStreamField[] fields = desc.getFields();
492:                    Object[] objVals = new Object[numFields];
493:                    numPrimFields = fields.length - objVals.length;
494:                    try {
495:                        GET_OBJ_FIELD_VALUES_METHOD.invoke(desc, new Object[] {
496:                                obj, objVals });
497:                    } catch (IllegalAccessException e) {
498:                        throw new RuntimeException(e);
499:                    } catch (InvocationTargetException e) {
500:                        throw new RuntimeException(e);
501:                    }
502:                    for (int i = 0; i < objVals.length; i++) {
503:                        if (objVals[i] instanceof  String
504:                                || objVals[i] instanceof  Number
505:                                || objVals[i] instanceof  Date
506:                                || objVals[i] instanceof  Boolean
507:                                || objVals[i] instanceof  Class) {
508:                            // fitler out common cases
509:                            continue;
510:                        }
511:
512:                        // Check for circular reference.
513:                        if (checked.containsKey(objVals[i])) {
514:                            continue;
515:                        }
516:
517:                        ObjectStreamField fieldDesc = fields[numPrimFields + i];
518:                        Field field;
519:                        try {
520:                            field = (Field) GET_FIELD_METHOD.invoke(fieldDesc,
521:                                    null);
522:                        } catch (IllegalAccessException e) {
523:                            throw new RuntimeException(e);
524:                        } catch (InvocationTargetException e) {
525:                            throw new RuntimeException(e);
526:                        }
527:
528:                        String fieldName = field.getName();
529:                        simpleName = field.getName();
530:                        fieldDescription = field.toString();
531:                        check(objVals[i]);
532:                    }
533:                }
534:            }
535:
536:            /**
537:             * @return name from root to current node concatted with slashes
538:             */
539:            private StringBuffer currentPath() {
540:                StringBuffer b = new StringBuffer();
541:                for (Iterator it = nameStack.iterator(); it.hasNext();) {
542:                    b.append(it.next());
543:                    if (it.hasNext()) {
544:                        b.append('/');
545:                    }
546:                }
547:                return b;
548:            }
549:
550:            /**
551:             * Dump with identation.
552:             * 
553:             * @param type
554:             *            the type that couldn't be serialized
555:             * @return A very pretty dump
556:             */
557:            private final String toPrettyPrintedStack(String type) {
558:                StringBuffer result = new StringBuffer();
559:                StringBuffer spaces = new StringBuffer();
560:                result.append("Unable to serialize class: ");
561:                result.append(type);
562:                result.append("\nField hierarchy is:");
563:                for (Iterator i = traceStack.listIterator(); i.hasNext();) {
564:                    spaces.append("  ");
565:                    TraceSlot slot = (TraceSlot) i.next();
566:                    result.append("\n").append(spaces).append(
567:                            slot.fieldDescription);
568:                    result.append(" [class=").append(
569:                            slot.object.getClass().getName());
570:                    if (slot.object instanceof  Component) {
571:                        Component component = (Component) slot.object;
572:                        result.append(", path=").append(component.getPath());
573:                    }
574:                    result.append("]");
575:                }
576:                result.append(" <----- field that is not serializable");
577:                return result.toString();
578:            }
579:
580:            /**
581:             * @see java.io.ObjectOutputStream#writeObjectOverride(java.lang.Object)
582:             */
583:            protected final void writeObjectOverride(Object obj)
584:                    throws IOException {
585:                if (!available) {
586:                    return;
587:                }
588:                root = obj;
589:                if (fieldDescription == null) {
590:                    fieldDescription = (root instanceof  Component) ? ((Component) root)
591:                            .getPath()
592:                            : "";
593:                }
594:
595:                check(root);
596:            }
597:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.