/** * VMware Continuent Tungsten Replicator * Copyright (C) 2015 VMware, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Initial developer(s): * Contributor(s): */ package com.continuent.tungsten.common.utils; /** * Kudos to Bob Lee for coming up with this nice utility for debugging * serialization issues. The original post was found here: * * http://crazybob.org/2007/02/debugging-serialization.html * * Also, try: * -Dsun.io.serialization.extendedDebugInfo=true */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class SerializationDebuggingUtils extends ObjectOutputStream { private static final Field DEPTH_FIELD; static { try { DEPTH_FIELD = ObjectOutputStream.class.getDeclaredField("depth"); DEPTH_FIELD.setAccessible(true); } catch (NoSuchFieldException e) { throw new AssertionError(e); } } final List<Class<?>> stack = new ArrayList<Class<?>>(); /** * Indicates whether or not OOS has tried to write an IOException * (presumably as the result of a serialization error) to the stream. */ boolean broken = false; public SerializationDebuggingUtils(OutputStream out) throws IOException { super(out); enableReplaceObject(true); } /** * Abuse {@code replaceObject()} as a hook to maintain our stack. */ protected Object replaceObject(Object o) { // ObjectOutputStream writes serialization // exceptions to the stream. Ignore // everything after that so we don't lose // the path to a non-serializable object. So // long as the user doesn't write an // IOException as the root object, we're OK. int currentDepth = currentDepth(); if (o instanceof IOException && currentDepth == 0) { broken = true; } if (!broken) { truncate(currentDepth); stack.add(o.getClass()); } return o; } private void truncate(int depth) { while (stack.size() > depth) { pop(); } } private Object pop() { return stack.remove(stack.size() - 1); } /** * Returns a 0-based depth within the object graph of the current object * being serialized. */ private int currentDepth() { try { Integer oneBased = ((Integer) DEPTH_FIELD.get(this)); return oneBased - 1; } catch (IllegalAccessException e) { throw new AssertionError(e); } } /** * Returns the path to the last object serialized. If an exception occurred, * this should be the path to the non-serializable object. */ public List<Class<?>> getStack() { return stack; } /* * Returns a nicely formatted stack. */ public String printStack() { StringBuilder builder = new StringBuilder(); for (Class<?> clazz : getStack()) { builder.append(clazz.getName()).append("\n"); } return builder.toString(); } public static Object testForSerialization(Object objToValidate) throws RuntimeException { SerializationDebuggingUtils obj_out = null; ObjectInputStream obj_in = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); obj_out = new SerializationDebuggingUtils(baos); obj_out.writeObject(objToValidate); byte[] buf = baos.toByteArray(); obj_out.close(); ByteArrayInputStream bais = new ByteArrayInputStream(buf); obj_in = new ObjectInputStream(bais); return obj_in.readObject(); } catch (Exception e) { String message = String.format( "Exception during serialization/deserialization of %s. Exeption=%s\n" + "Path to bad object:\n%s\n", objToValidate .getClass().getName(), e, obj_out.printStack()); System.out.println(message); throw new RuntimeException(message); } } public static boolean canBeSerialized(Object objToValidate) { try { testForSerialization(objToValidate); } catch (RuntimeException e) { return false; } return true; } }