Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
Licensed to the Austrian Association for Software Tool Integration (AASTI) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The AASTI licenses this file to you 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.
 
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
  * The ASF licenses this file to You 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.
  */
 
 package org.openengsb.core.persistence.internal;
 
 import java.util.Date;
 import java.util.Map;
 import java.util.Set;
 
Utility class that analyzes objects for non-serializable nodes. Construct, then call check(java.lang.Object) with the object you want to check. When a non-serializable object is found, a WicketNotSerializableException is thrown with a message that shows the trace up to the not-serializable object. The exception is thrown for the first non-serializable instance it encounters, so multiple problems will not be shown.

As this class depends heavily on JDK's serialization internals using introspection, analyzing may not be possible, for instance when the runtime environment does not have sufficient rights to set fields accessible that would otherwise be hidden. You should call isAvailable() to see whether this class can operate properly. If it doesn't, you should fall back to e.g. re-throwing/ printing the java.io.NotSerializableException you probably got before using this class.

Author(s):
eelcohillenius
Al Maw
 
 public final class SerializableChecker extends ObjectOutputStream {

    
log.
 
     private static final Logger LOGGER = LoggerFactory.getLogger(SerializableChecker.class);

    
Exception that is thrown when a non-serializable object was found.
 
     public static final class ObjectDbNotSerializableException extends RuntimeException {
         private static final long serialVersionUID = 1L;
 
         ObjectDbNotSerializableException(String messageThrowable cause) {
             super(messagecause);
         }
     }

    
Does absolutely nothing.
 
     private static class NoopOutputStream extends OutputStream {
         @Override
         public void close() {
        }
        @Override
        public void flush() {
        }
        @Override
        public void write(byte[] b) {
        }
        @Override
        public void write(byte[] bint iint l) {
        }
        @Override
        public void write(int b) {
        }
    }
    private abstract static class ObjectOutputAdaptor implements ObjectOutput {
        @Override
        public void writeBoolean(boolean vthrows IOException {
        }
        @Override
        public void writeByte(int vthrows IOException {
        }
        @Override
        public void writeShort(int vthrows IOException {
        }
        @Override
        public void writeChar(int vthrows IOException {
        }
        @Override
        public void writeInt(int vthrows IOException {
        }
        @Override
        public void writeLong(long vthrows IOException {
        }
        @Override
        public void writeFloat(float vthrows IOException {
        }
        @Override
        public void writeDouble(double vthrows IOException {
        }
        @Override
        public void writeBytes(String sthrows IOException {
        }
        @Override
        public void writeChars(String sthrows IOException {
        }
        @Override
        public void writeUTF(String sthrows IOException {
        }
        @Override
        public void writeObject(Object objthrows IOException {
        }
        @Override
        public void write(int bthrows IOException {
        }
        @Override
        public void write(byte[] bthrows IOException {
        }
        @Override
        public void write(byte[] bint offint lenthrows IOException {
        }
        @Override
        public void flush() throws IOException {
        }
        @Override
        public void close() throws IOException {
        }
    }

    
Holds information about the field and the resulting object being traced.
    private static final class TraceSlot {
        private final String fieldDescription;
        private final Object object;
        TraceSlot(Object objectString fieldDescription) {
            super();
            this. = object;
            this. = fieldDescription;
        }
        @Override
        public String toString() {
            return .getClass() + " - " + ;
        }
    }
    private static final NoopOutputStream DUMMY_OUTPUT_STREAM = new NoopOutputStream();

    
Whether we can execute the tests. If false, check will just return.
    private static boolean available = true;
    // this hack - accessing the serialization API through introspection - is
    // the only way to use Java serialization for our purposes without writing
    // the whole thing from scratch (and even then, it would be limited). This
    // way of working is of course fragile for internal API changes, but as we
    // do an extra check on availability and we report when we can't use this
    // introspection fu, we'll find out soon enough and clients on this class
    // can fall back on Java's default exception for serialization errors (which
    // sucks and is the main reason for this attempt).
    private static Method lookupMethod;
    private static Method getClassDataLayoutMethod;
    private static Method getNumObjFieldsMethod;
    private static Method getObjFieldValuesMethod;
    private static Method getFieldMethod;
    private static Method hasWriteReplaceMethodMetod;
    private static Method invokeWriteReplaceMethod;
    static {
        try {
             = ObjectStreamClass.class.getDeclaredMethod("lookup"new Class[]{
                Class.class. });
            .setAccessible(true);
                "getClassDataLayout", (Class[]) null);
            .setAccessible(true);
                "getNumObjFields", (Class[]) null);
            .setAccessible(true);
                "getObjFieldValues"new Class[]{ Object.classObject[].class });
            .setAccessible(true);
             = ObjectStreamField.class.getDeclaredMethod("getField", (Class[]) null);
            .setAccessible(true);
                "hasWriteReplaceMethod", (Class[]) null);
            .setAccessible(true);
                "invokeWriteReplace"new Class[]{ Object.class });
            .setAccessible(true);
        } catch (Exception e) {
            .warn("SerializableChecker not available"e);
             = false;
        }
    }

    
Gets whether we can execute the tests. If false, calling check(java.lang.Object) will just return and you are advised to rely on the java.io.NotSerializableException. Clients are advised to call this method prior to calling the check method.

Returns:
whether security settings and underlying API etc allow for accessing the serialization API using introspection
    public static boolean isAvailable() {
        return ;
    }

    
object stack that with the trace path.
    private final LinkedList<TraceSlottraceStack = new LinkedList<TraceSlot>();

    
set for checking circular references.
    private final Map<ObjectObjectchecked = new IdentityHashMap<ObjectObject>();

    
string stack with current names pushed.
    private final LinkedList<StringnameStack = new LinkedList<String>();

    
root object being analyzed.
    private Object root;

    
set of classes that had no writeObject methods at lookup (to avoid repeated checking)
    private final Set<Class<?>> writeObjectMethodMissing = new HashSet<Class<?>>();

    
current simple field name.
    private String simpleName = "";

    
current full field description.
    private String fieldDescription;

    
Exception that should be set as the cause when throwing a new exception.
    private final NotSerializableException exception;
    private final Stack<Objectstack = new Stack<Object>();

    
Construct.

Parameters:
exception exception that should be set as the cause when throwing a new exception
Throws:
java.io.IOException
    public SerializableChecker(NotSerializableException exceptionthrows IOException {
        this. = exception;
    }

    
    @Override
    public void reset() throws IOException {
         = null;
        .clear();
         = null;
         = null;
        .clear();
        .clear();
    }
    private void check(Object obj) {
        if (obj == null) {
            return;
        }
        try {
            if (.contains(obj)) {
                return;
            }
        } catch (RuntimeException e) {
            .warn("Wasn't possible to check the object " + obj.getClass()
                    + " possible due an problematic implementation of equals method");
            /*
             * Can't check if this obj were in stack, giving up because we don't want to throw an invaluable exception
             * to user. The main goal of this checker is to find non serializable data
             */
            return;
        }
        .push(obj);
        try {
            internalCheck(obj);
        } finally {
            .pop();
        }
    }
    private void internalCheck(Object obj) {
        if (obj == null) {
            return;
        }
        Class<?> cls = obj.getClass();
        .add();
        .add(new TraceSlot(obj));
        if (!(obj instanceof Serializable) && (!Proxy.isProxyClass(cls))) {
            throw new ObjectDbNotSerializableException(toPrettyPrintedStack(obj.getClass().getName()), );
        }
        ObjectStreamClass desc;
        for (;;) {
            try {
                desc = (ObjectStreamClass.invoke(nullcls.);
                obj = .invoke(descobj);
                Class<?> repCl = obj.getClass();
                if (!(Boolean.invoke(desc, (Object[]) null)
                        || obj == null
                        || repCl == cls) {
                    break;
                }
                cls = repCl;
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
        if (cls.isPrimitive()) {
            .trace("skip primitive check");
        } else if (cls.isArray()) {
            .put(objnull);
            Class<?> ccl = cls.getComponentType();
            if (!(ccl.isPrimitive())) {
                Object[] objs = (Object[]) obj;
                for (int i = 0; i < objs.lengthi++) {
                    String arrayPos = "[" + i + "]";
                     = arrayPos;
                     += arrayPos;
                    check(objs[i]);
                }
            }
        } else if (obj instanceof Externalizable && (!Proxy.isProxyClass(cls))) {
            Externalizable extObj = (Externalizableobj;
            try {
                extObj.writeExternal(new ObjectOutputAdaptor() {
                    private int count = 0;
                    @Override
                    public void writeObject(Object streamObjthrows IOException {
                        if (.containsKey(streamObj)) {
                            return;
                        }
                        .put(streamObjnull);
                        String arrayPos = "[write:" + ++ + "]";
                         = arrayPos;
                         += arrayPos;
                        check(streamObj);
                    }
                });
            } catch (Exception e) {
                if (e instanceof ObjectDbNotSerializableException) {
                    throw (ObjectDbNotSerializableExceptione;
                }
                .warn("error delegating to Externalizable : " + e.getMessage() + ", path: " + currentPath());
            }
        } else {
            Method writeObjectMethod = null;
            if (!.contains(cls)) {
                try {
                    writeObjectMethod =
                        cls.getDeclaredMethod("writeObject"new Class[]{ java.io.ObjectOutputStream.class });
                } catch (SecurityException e) {
                    .add(cls);
                } catch (NoSuchMethodException e) {
                    .add(cls);
                }
            }
            final Object original = obj;
            if (writeObjectMethod != null) {
                class InterceptingObjectOutputStream extends ObjectOutputStream {
                    private int counter;
                    InterceptingObjectOutputStream() throws IOException {
                        super();
                        enableReplaceObject(true);
                    }
                    @Override
                    protected Object replaceObject(Object streamObjthrows IOException {
                        if (streamObj == original) {
                            return streamObj;
                        }
                        ++;
                        if (.containsKey(streamObj)) {
                            return null;
                        }
                        .put(streamObjnull);
                        String arrayPos = "[write:" +  + "]";
                         = arrayPos;
                         += arrayPos;
                        check(streamObj);
                        return streamObj;
                    }
                }
                try {
                    InterceptingObjectOutputStream ioos = new InterceptingObjectOutputStream();
                    ioos.writeObject(obj);
                } catch (Exception e) {
                    if (e instanceof ObjectDbNotSerializableException) {
                        throw (ObjectDbNotSerializableExceptione;
                    }
                    .warn("error delegating to writeObject : " + e.getMessage() + ", path: " + currentPath());
                }
            } else {
                Object[] slots;
                try {
                    slots = (Object[]) .invoke(desc, (Object[]) null);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                for (Object slot : slots) {
                    ObjectStreamClass slotDesc;
                    try {
                        Field descField = slot.getClass().getDeclaredField("desc");
                        descField.setAccessible(true);
                        slotDesc = (ObjectStreamClassdescField.get(slot);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    .put(objnull);
                    checkFields(objslotDesc);
                }
            }
        }
        .removeLast();
        .removeLast();
    }
    private void checkFields(Object objObjectStreamClass desc) {
        int numFields;
        try {
            numFields = (Integer.invoke(desc, (Object[]) null);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        if (numFields > 0) {
            int numPrimFields;
            ObjectStreamField[] fields = desc.getFields();
            Object[] objVals = new Object[numFields];
            numPrimFields = fields.length - objVals.length;
            try {
                .invoke(descobjobjVals);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
            for (int i = 0; i < objVals.lengthi++) {
                if (objVals[iinstanceof String || objVals[iinstanceof Number || objVals[iinstanceof Date
                        || objVals[iinstanceof Boolean || objVals[iinstanceof Class) {
                    continue;
                }
                // Check for circular reference.
                if (.containsKey(objVals[i])) {
                    continue;
                }
                ObjectStreamField fieldDesc = fields[numPrimFields + i];
                Field field;
                try {
                    field = (Field.invoke(fieldDesc, (Object[]) null);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
                field.getName();
                 = field.getName();
                 = field.toString();
                check(objVals[i]);
            }
        }
    }

    

Returns:
name from root to current node concatenated with slashes
    private StringBuilder currentPath() {
        StringBuilder b = new StringBuilder();
        for (Iterator<Stringit = .iterator(); it.hasNext();) {
            b.append(it.next());
            if (it.hasNext()) {
                b.append('/');
            }
        }
        return b;
    }

    
Dump with indentation.

Parameters:
type the type that couldn't be serialized
Returns:
A very pretty dump
    private String toPrettyPrintedStack(String type) {
        StringBuilder result = new StringBuilder();
        StringBuilder spaces = new StringBuilder();
        result.append("Unable to serialize class: ");
        result.append(type);
        result.append("\nField hierarchy is:");
        for (Iterator<TraceSloti = .listIterator(); i.hasNext();) {
            spaces.append("  ");
            TraceSlot slot = i.next();
            result.append("\n").append(spaces).append(slot.fieldDescription);
            result.append(" [class=").append(slot.object.getClass().getName());
            result.append("]");
        }
        result.append(" <----- field that is not serializable");
        return result.toString();
    }

    
    @Override
    protected void writeObjectOverride(Object objthrows IOException {
        if (!) {
            return;
        }
         = obj;
        check();
    }
New to GrepCode? Check out our FAQ X