All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.util.lang.WicketObjects Maven / Gradle / Ivy

Go to download

Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and unloading of Wicket components and pageSources.

There is a newer version: 5.0.0
Show newest version
/*
 * 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.lang;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.HashMap;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.application.IClassResolver;
import org.apache.wicket.settings.IApplicationSettings;
import org.apache.wicket.util.io.ByteCountingOutputStream;
import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Object (de)serialization utilities.
 */
public class WicketObjects
{
	/** log. */
	private static final Logger log = LoggerFactory.getLogger(WicketObjects.class);

	private WicketObjects()
	{
	}

	/**
	 * @param 
	 *            class type
	 * @param className
	 *            Class to resolve
	 * @return Resolved class
	 * @throws ClassNotFoundException
	 */
	@SuppressWarnings("unchecked")
	public static  Class resolveClass(final String className)
	{
		Class resolved = null;
		try
		{
			if (Application.exists())
			{
				resolved = (Class)Application.get()
					.getApplicationSettings()
					.getClassResolver()
					.resolveClass(className);
			}

			if (resolved == null)
			{
				resolved = (Class)Class.forName(className, false, Thread.currentThread()
					.getContextClassLoader());
			}
		}
		catch (ClassNotFoundException cnfx)
		{
			log.warn("Could not resolve class [" + className + "]", cnfx);
		}
		return resolved;
	}

	/**
	 * Interface that enables users to plugin the way object sizes are calculated with Wicket.
	 */
	public static interface IObjectSizeOfStrategy
	{
		/**
		 * Computes the size of an object. This typically is an estimation, not an absolute accurate
		 * size.
		 * 
		 * @param object
		 *            The serializable object to compute size of
		 * @return The size of the object in bytes.
		 */
		long sizeOf(Serializable object);
	}

	/**
	 * {@link IObjectSizeOfStrategy} that works by serializing the object to an instance of
	 * {@link ByteCountingOutputStream}, which records the number of bytes written to it. Hence,
	 * this gives the size of the object as it would be serialized,including all the overhead of
	 * writing class headers etc. Not very accurate (the real memory consumption should be lower)
	 * but the best we can do in a cheap way pre JDK 5.
	 */
	public static final class SerializingObjectSizeOfStrategy implements IObjectSizeOfStrategy
	{
		/**
		 * @see org.apache.wicket.util.lang.WicketObjects.IObjectSizeOfStrategy#sizeOf(java.io.Serializable)
		 */
		public long sizeOf(Serializable object)
		{
			if (object == null)
			{
				return 0;
			}
			try
			{
				final ByteCountingOutputStream out = new ByteCountingOutputStream();
				new ObjectOutputStream(out).writeObject(object);
				out.close();
				return out.size();
			}
			catch (IOException e)
			{
				if (log.isWarnEnabled())
				{
					log.warn("Unable to determine object size: " + object.toString(), e);
				}
				return -1;
			}
		}

	}

	private static final class ReplaceObjectInputStream extends ObjectInputStream
	{
		private final ClassLoader classloader;
		private final HashMap replacedComponents;

		private ReplaceObjectInputStream(InputStream in,
			HashMap replacedComponents, ClassLoader classloader)
			throws IOException
		{
			super(in);
			this.replacedComponents = replacedComponents;
			this.classloader = classloader;
			enableResolveObject(true);
		}

		// This override is required to resolve classes inside in different
		// bundle, i.e.
		// The classes can be resolved by OSGI classresolver implementation
		@Override
		protected Class resolveClass(ObjectStreamClass desc) throws IOException,
			ClassNotFoundException
		{
			String className = desc.getName();

			try
			{
				return Class.forName(className, true, classloader);
			}
			catch (ClassNotFoundException ex1)
			{
				// ignore this exception.
				log.debug("Class not found by using objects own classloader, trying the IClassResolver");
			}

			Application application = Application.get();
			IApplicationSettings applicationSettings = application.getApplicationSettings();
			IClassResolver classResolver = applicationSettings.getClassResolver();

			Class candidate = null;
			try
			{
				candidate = classResolver.resolveClass(className);
				if (candidate == null)
				{
					candidate = super.resolveClass(desc);
				}
			}
			catch (WicketRuntimeException ex)
			{
				if (ex.getCause() instanceof ClassNotFoundException)
				{
					throw (ClassNotFoundException)ex.getCause();
				}
			}
			return candidate;
		}

		@Override
		protected Object resolveObject(Object obj) throws IOException
		{
			Object replaced = replacedComponents.get(obj);
			if (replaced != null)
			{
				return replaced;
			}
			return super.resolveObject(obj);
		}
	}

	private static final class ReplaceObjectOutputStream extends ObjectOutputStream
	{
		private final HashMap replacedComponents;

		private ReplaceObjectOutputStream(OutputStream out,
			HashMap replacedComponents) throws IOException
		{
			super(out);
			this.replacedComponents = replacedComponents;
			enableReplaceObject(true);
		}

		@Override
		protected Object replaceObject(Object obj) throws IOException
		{
			if (obj instanceof Component)
			{
				final Component component = (Component)obj;
				String name = component.getPath();
				replacedComponents.put(name, component);
				return name;
			}
			return super.replaceObject(obj);
		}
	}

	/**
	 * Makes a deep clone of an object by serializing and deserializing it. The object must be fully
	 * serializable to be cloned. This method will not clone wicket Components, it will just reuse
	 * those instances so that the complete component tree is not copied over only the model data.
	 * 
	 * @param object
	 *            The object to clone
	 * @return A deep copy of the object
	 */
	public static Object cloneModel(final Object object)
	{
		if (object == null)
		{
			return null;
		}
		else
		{
			try
			{
				final ByteArrayOutputStream out = new ByteArrayOutputStream(256);
				final HashMap replacedObjects = Generics.newHashMap();
				ObjectOutputStream oos = new ReplaceObjectOutputStream(out, replacedObjects);
				oos.writeObject(object);
				ObjectInputStream ois = new ReplaceObjectInputStream(new ByteArrayInputStream(
					out.toByteArray()), replacedObjects, object.getClass().getClassLoader());
				return ois.readObject();
			}
			catch (ClassNotFoundException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
			catch (IOException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
		}
	}

	/**
	 * Strategy for calculating sizes of objects. Note: I didn't make this an application setting as
	 * we have enough of those already, and the typical way this probably would be used is that
	 * install a different one according to the JDK version used, so varying them between
	 * applications doesn't make a lot of sense.
	 */
	private static IObjectSizeOfStrategy objectSizeOfStrategy = new SerializingObjectSizeOfStrategy();

	/**
	 * Makes a deep clone of an object by serializing and deserializing it. The object must be fully
	 * serializable to be cloned. No extra debug info is gathered.
	 * 
	 * @param object
	 *            The object to clone
	 * @return A deep copy of the object
	 * @see #cloneModel(Object)
	 */
	public static Object cloneObject(final Object object)
	{
		if (object == null)
		{
			return null;
		}
		else
		{
			try
			{
				final ByteArrayOutputStream out = new ByteArrayOutputStream(256);
				ObjectOutputStream oos = new ObjectOutputStream(out);
				oos.writeObject(object);
				ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
					out.toByteArray()))
				{
					// This override is required to resolve classes inside in different bundle, i.e.
					// The classes can be resolved by OSGI classresolver implementation
					@Override
					protected Class resolveClass(ObjectStreamClass desc) throws IOException,
						ClassNotFoundException
					{
						String className = desc.getName();

						try
						{
							return Class.forName(className, true, object.getClass()
								.getClassLoader());
						}
						catch (ClassNotFoundException ex1)
						{
							// ignore this exception.
							log.debug("Class not found by using objects own classloader, trying the IClassResolver");
						}


						Application application = Application.get();
						IApplicationSettings applicationSettings = application.getApplicationSettings();
						IClassResolver classResolver = applicationSettings.getClassResolver();

						Class candidate = null;
						try
						{
							candidate = classResolver.resolveClass(className);
							if (candidate == null)
							{
								candidate = super.resolveClass(desc);
							}
						}
						catch (WicketRuntimeException ex)
						{
							if (ex.getCause() instanceof ClassNotFoundException)
							{
								throw (ClassNotFoundException)ex.getCause();
							}
						}
						return candidate;
					}
				};
				return ois.readObject();
			}
			catch (ClassNotFoundException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
			catch (IOException e)
			{
				throw new WicketRuntimeException("Internal error cloning object", e);
			}
		}
	}

	/**
	 * Creates a new instance using the current application's class resolver. Returns null if
	 * className is null.
	 * 
	 * @param className
	 *            The full class name
	 * @return The new object instance
	 */
	public static Object newInstance(final String className)
	{
		if (!Strings.isEmpty(className))
		{
			try
			{
				Class c = WicketObjects.resolveClass(className);
				return c.newInstance();
			}
			catch (Exception e)
			{
				throw new WicketRuntimeException("Unable to create " + className, e);
			}
		}
		return null;
	}

	/**
	 * Sets the strategy for determining the sizes of objects.
	 * 
	 * @param objectSizeOfStrategy
	 *            the strategy. Pass null to reset to the default.
	 */
	public static void setObjectSizeOfStrategy(IObjectSizeOfStrategy objectSizeOfStrategy)
	{
		if (objectSizeOfStrategy == null)
		{
			WicketObjects.objectSizeOfStrategy = new SerializingObjectSizeOfStrategy();
		}
		else
		{
			WicketObjects.objectSizeOfStrategy = objectSizeOfStrategy;
		}
		log.info("using " + objectSizeOfStrategy + " for calculating object sizes");
	}

	/**
	 * Computes the size of an object. Note that this is an estimation, never an absolute accurate
	 * size.
	 * 
	 * @param object
	 *            Object to compute size of
	 * @return The size of the object in bytes
	 */
	public static long sizeof(final Serializable object)
	{
		return objectSizeOfStrategy.sizeOf(object);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy