org.apache.wicket.util.io.SerializableChecker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.ops4j.pax.wicket.service Show documentation
Show all versions of org.ops4j.pax.wicket.service Show documentation
Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and
unloading of Wicket components and pageSources.
/*
* 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.apache.wicket.util.io;
import java.io.Externalizable;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class that analyzes objects for non-serializable nodes. Construct, then call
* {@link #check(Object)} with the object you want to check. When a non-serializable object is
* found, a {@link 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
* {@link SerializableChecker#isAvailable()} to see whether this class can operate properly. If it
* doesn't, you should fall back to e.g. re-throwing/ printing the {@link NotSerializableException}
* you probably got before using this class.
*
*
* @author eelcohillenius
* @author Al Maw
*/
public final class SerializableChecker extends ObjectOutputStream
{
/** log. */
private static final Logger log = LoggerFactory.getLogger(SerializableChecker.class);
/**
* Exception that is thrown when a non-serializable object was found.
*/
public static final class WicketNotSerializableException extends WicketRuntimeException
{
private static final long serialVersionUID = 1L;
WicketNotSerializableException(String message, Throwable cause)
{
super(message, cause);
}
}
/**
* 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[] b, int i, int l)
{
}
@Override
public void write(int b)
{
}
}
private static abstract class ObjectOutputAdaptor implements ObjectOutput
{
public void close() throws IOException
{
}
public void flush() throws IOException
{
}
public void write(byte[] b) throws IOException
{
}
public void write(byte[] b, int off, int len) throws IOException
{
}
public void write(int b) throws IOException
{
}
public void writeBoolean(boolean v) throws IOException
{
}
public void writeByte(int v) throws IOException
{
}
public void writeBytes(String s) throws IOException
{
}
public void writeChar(int v) throws IOException
{
}
public void writeChars(String s) throws IOException
{
}
public void writeDouble(double v) throws IOException
{
}
public void writeFloat(float v) throws IOException
{
}
public void writeInt(int v) throws IOException
{
}
public void writeLong(long v) throws IOException
{
}
public void writeShort(int v) throws IOException
{
}
public void writeUTF(String str) 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 object, String fieldDescription)
{
super();
this.object = object;
this.fieldDescription = fieldDescription;
}
@Override
public String toString()
{
return object.getClass() + " - " + fieldDescription;
}
}
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 LOOKUP_METHOD;
private static Method GET_CLASS_DATA_LAYOUT_METHOD;
private static Method GET_NUM_OBJ_FIELDS_METHOD;
private static Method GET_OBJ_FIELD_VALUES_METHOD;
private static Method GET_FIELD_METHOD;
private static Method HAS_WRITE_REPLACE_METHOD_METHOD;
private static Method INVOKE_WRITE_REPLACE_METHOD;
static
{
try
{
LOOKUP_METHOD = ObjectStreamClass.class.getDeclaredMethod("lookup", new Class[] {
Class.class, Boolean.TYPE });
LOOKUP_METHOD.setAccessible(true);
GET_CLASS_DATA_LAYOUT_METHOD = ObjectStreamClass.class.getDeclaredMethod(
"getClassDataLayout", (Class[])null);
GET_CLASS_DATA_LAYOUT_METHOD.setAccessible(true);
GET_NUM_OBJ_FIELDS_METHOD = ObjectStreamClass.class.getDeclaredMethod(
"getNumObjFields", (Class[])null);
GET_NUM_OBJ_FIELDS_METHOD.setAccessible(true);
GET_OBJ_FIELD_VALUES_METHOD = ObjectStreamClass.class.getDeclaredMethod(
"getObjFieldValues", new Class[] { Object.class, Object[].class });
GET_OBJ_FIELD_VALUES_METHOD.setAccessible(true);
GET_FIELD_METHOD = ObjectStreamField.class.getDeclaredMethod("getField", (Class[])null);
GET_FIELD_METHOD.setAccessible(true);
HAS_WRITE_REPLACE_METHOD_METHOD = ObjectStreamClass.class.getDeclaredMethod(
"hasWriteReplaceMethod", (Class[])null);
HAS_WRITE_REPLACE_METHOD_METHOD.setAccessible(true);
INVOKE_WRITE_REPLACE_METHOD = ObjectStreamClass.class.getDeclaredMethod(
"invokeWriteReplace", new Class[] { Object.class });
INVOKE_WRITE_REPLACE_METHOD.setAccessible(true);
}
catch (Exception e)
{
log.warn("SerializableChecker not available", e);
available = false;
}
}
/**
* Gets whether we can execute the tests. If false, calling {@link #check(Object)} will just
* return and you are advised to rely on the {@link NotSerializableException}. Clients are
* advised to call this method prior to calling the check method.
*
* @return whether security settings and underlying API etc allow for accessing the
* serialization API using introspection
*/
public static boolean isAvailable()
{
return available;
}
/** object stack that with the trace path. */
private final LinkedList traceStack = new LinkedList();
/** set for checking circular references. */
private final Map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy