/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintStream; import java.io.Serializable; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashSet; import java.util.Set; import xxl.core.io.NullOutputStream; import xxl.core.io.converters.Converter; import xxl.core.util.metaData.CompositeMetaData; import xxl.core.util.metaData.MetaDataException; /** * The <code>XXLSystem</code> class contains system related methods. For * example there are static methods to determine the memory size of an object. * It cannot be instantiated. * * @see java.util.HashSet * @see java.util.Set * @see java.lang.reflect.Array * @see java.lang.reflect.Field * @see java.lang.reflect.Modifier */ public class XXLSystem { /** * Don't let anyone instantiate this class. */ private XXLSystem() { // ensure non-instantiability } /** * The "standard" null stream. This stream is already open and ready to * accept output data. Typically this stream corresponds to suppress any * display output. This stream fills the gap in {@link java.lang.System} * providing "only" standard {@link java.io.PrintStream print streams} * for standard out and errors but no "null sink". * * @see PrintStream#println() */ public static final PrintStream NULL = new PrintStream( NullOutputStream.NULL); /** * The default size of a reference in memory. */ public final static int REFERENCE_MEM_SIZE = 8; /** * This class is a Wrapper for objects with the intention to distinguish * two objects if and only if they refer to the same object. That means for * any reference values <code>x</code> and <code>y</code>, the method * <code>equals()</code> returns <code>true</code> if and only if * <code>x</code> and <code>y</code> refer to the same object * (<code>x==y</code> has the value <code>true</code>). * * <p>It is used in the method * {@link #sizeOf(Object, int, Set, Set, Set, int)} where attributes of the * object to be analyzed are inserted in a set with the intention to create * an exact image of the memory allocated for this given object. Each * non-primitive attribute is wrapped using this class before inserting it * into the set, so the set contains only one instance of each traversed * class during determining the object size. Because of wrapping the * non-primitive attributes the <code>equals</code>-method of this class is * used, in contradiction to inserting the objects directly in the set, the * attributes's own <code>equals</code>-method will be used, but this may * lead to duplicates, like the following example demonstrates: * <pre><code> * class Test { * * public Integer a, b; * * public Test(Integer a, Integer b) { * this.a = a; * this.b = b; * } * * public static void main(String[] args) throws Exception { * Test test = new Test(new Integer(7), new Integer(7)); * System.out.println("a == b ? " + test.a == test.b); * System.out.println("a.equals(b) ? " + test.a.equals(test.b)); * System.out.println("getObjectSize(test) = " + XXLSystem.getObjectSize(test)); * } * } * </code></pre> * The output of this short example is: * <pre> * a==b ? false * a.equals(b) ? true * getObjectSize(test) = 32 * </pre> * If the attributes inserted into the HashSet were not wrapped, * the returned object size would only be 28, because * the attribute <tt>a</tt> was detected to be equal to attribute <tt>b</tt>, * and therefore only a reference pointing to the same object would be * saved in memory. * * @see #sizeOf(Object, int, Set, Set, Set, int) */ protected static final class Wrapper { /** * The wrapped object. */ public final Object object; /** * Creates a new Wrapper * by wrapping the given object. * * @param object the object to be wrapped. */ public Wrapper(Object object) { this.object = object; } /** * The <tt>equals</tt> method for class <code>Object</code> implements * the most discriminating possible equivalence relation on objects; * that is, for any reference values <code>x</code> and <code>y</code>, * this method returns <code>true</code> if and only if <code>x</code> and * <code>y</code> refer to the same object (<code>x==y</code> has the * value <code>true</code>). * * @param wrapper the reference object with which to compare. * @return <code>true</code> if this object is the same as * the wrapper argument; <code>false</code> otherwise. */ @Override public boolean equals(Object wrapper) { return this == wrapper ? true : wrapper == null || !(wrapper instanceof Wrapper) ? false : object == ((Wrapper)wrapper).object; } /** * Returns a hash code value for the wrapped object. * This method is supported for the benefit * of hashtables such as those provided by * <code>java.util.Hashtable</code>. * * @return a hash code value for this object. * @see java.lang.Object#hashCode() * @see java.util.Hashtable */ @Override public int hashCode () { return object.hashCode(); } @Override public String toString() { return "Wrapper(" + object.toString() + ")"; } } /** * The size of an object is computed recursively, by determining the * attributes of the class the object is an instance of. If the given * object is an array the sizes of the array's components are computed * recursively and the resulting memory size is incremented by 4 bytes * (needed to save the length information of an array). Otherwise the * algorithm determines the attributes (fields) of the object's class and * its super classes. If an attribute's type is primitive, the according * size is added to the object's memory size. Otherwise the attribute is an * objects and its memory size is calculated recursively and added to the * object's memory size. * * <p><b>Overview:</b><br><br> * * <table> * <tr><th align=left colspan=3 style="font-weight:normal">size of primitive types</th></tr> * <tr><td>byte, boolean</td><td> : </td><td>1 byte</td></tr> * <tr><td>short, char</td><td> : </td><td>2 bytes</td></tr> * <tr><td>int, float</td><td> : </td><td>4 bytes</td></tr> * <tr><td>long, double</td><td> : </td><td>8 bytes</td></tr> * </table><br><br> * * <table> * <tr><th align=left colspan=3 style="font-weight:normal">size of references depends on the platform</th></tr> * <tr><td>32-bit platform</td><td> : </td><td>4 bytes</td></tr> * <tr><td>64-bit platform</td><td> : </td><td>8 bytes</td></tr> * </table><br><br> * * size of primitive arrays: 4 bytes + (array.length * size of primitive type)<br><br> * * size of object arrays : 4 bytes + Σ(size of components)<br><br> * * All traversed attributes are inserted in a set, which represents an * exact image of the allocated memory concerning the given object.<p> * * @param object the object the memory size is to be determined. * @param referenceSize the memory size of a reference. * @param processedObjects a set containing the traversed attributes * (required for cycle detection). * @param excludedClasses a set of classes which are excluded during memory * measurement. * @param excludedFields a set of fields which are excluded during memory * measurement. * @param excludedFieldModifiers a bitmap identifying the * {@link Modifier modifiers} of the fields that should be excluded * from memory measurement. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int sizeOf(Object object, int referenceSize, Set<? super Wrapper> processedObjects, Set<Class<?>> excludedClasses, Set<Field> excludedFields, int excludedFieldModifiers) throws IllegalAccessException { int size = 0; // Circle detection if (object == null || !processedObjects.add(new Wrapper(object))) return size; Class<?> objectClass = object.getClass(); // refresh object-reference if (objectClass.isArray()) { int length = Array.getLength(object); size += 4; // allocated for saving length objectClass = objectClass.getComponentType(); if (objectClass.isPrimitive()) size += objectClass == Boolean.TYPE ? Math.ceil(length / 8.0) : length * (objectClass == Byte.TYPE ? 1 : objectClass == Character.TYPE || objectClass == Short.TYPE ? 2 : objectClass == Integer.TYPE || objectClass == Float.TYPE ? 4 : 8); else for (int i = 0; i < length; i++) size += sizeOf(Array.get(object, i), referenceSize, processedObjects, excludedClasses, excludedFields, excludedFieldModifiers); } else for (; objectClass != null; objectClass = objectClass.getSuperclass()) for (Field field : objectClass.getDeclaredFields()) { objectClass = field.getType(); if (!excludedClasses.contains(objectClass) && !excludedFields.contains(field) && (field.getModifiers() & excludedFieldModifiers) == 0) if (objectClass.isPrimitive()) size += objectClass == Boolean.TYPE || objectClass == Byte.TYPE ? 1 : objectClass == Character.TYPE || objectClass == Short.TYPE ? 2 : objectClass == Integer.TYPE || objectClass == Float.TYPE ? 4 : 8; else { field.setAccessible(true); size += referenceSize + sizeOf(field.get(object), referenceSize, processedObjects, excludedClasses, excludedFields, excludedFieldModifiers); } } return size; } /** * The size of an object is computed recursively, by determining the * attributes of the class the object is an instance of. If the given * object is an array the sizes of the array's components are computed * recursively and the resulting memory size is incremented by 4 bytes * (needed to save the length information of an array). Otherwise the * algorithm determines the attributes (fields) of the object's class and * its super classes. If an attribute's type is primitive, the according * size is added to the object's memory size. Otherwise the attribute is an * objects and its memory size is calculated recursively and added to the * object's memory size. * * <p><b>Overview:</b><br><br> * * <table> * <tr><th align=left colspan=3 style="font-weight:normal">size of primitive types</th></tr> * <tr><td>byte, boolean</td><td> : </td><td>1 byte</td></tr> * <tr><td>short, char</td><td> : </td><td>2 bytes</td></tr> * <tr><td>int, float</td><td> : </td><td>4 bytes</td></tr> * <tr><td>long, double</td><td> : </td><td>8 bytes</td></tr> * </table><br><br> * * <table> * <tr><th align=left colspan=3 style="font-weight:normal">size of references depends on the platform</th></tr> * <tr><td>32-bit platform</td><td> : </td><td>4 bytes</td></tr> * <tr><td>64-bit platform</td><td> : </td><td>8 bytes</td></tr> * </table><br><br> * * size of primitive arrays: 4 bytes + (array.length * size of primitive type)<br><br> * * size of object arrays : 4 bytes + Σ(size of components)<br><br> * * All traversed attributes are inserted in a set, which represents an * exact image of the allocated memory concerning the given object.<p> * * @param object the object the memory size is to be determined. * @param referenceSize the memory size of a reference. * @param processedObjects a set containing the traversed attributes * (required for cycle detection). * @param excludedClasses a set of classes which are excluded during memory * measurement. * @param excludedFields a set of fields which are excluded during memory * measurement. * @param excludedFieldModifiers the {@link Modifier modifiers} of the * fields that should be excluded from memory measurement. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int sizeOf(Object object, int referenceSize, Set<? super Wrapper> processedObjects, Set<Class<?>> excludedClasses, Set<Field> excludedFields, int... excludedFieldModifiers) throws IllegalAccessException { int bitset = 0; for (int excludedFieldModifier : excludedFieldModifiers) bitset |= excludedFieldModifier; return sizeOf(object, referenceSize, processedObjects, excludedClasses, excludedFields, bitset); } /** * The main memory size of the given object is computed recursively, by * determining the attributes of the class the object is an instance of. * The size of references on different platforms (i.e. 32-bit and 64-bit * platforms respectively) can be specified separately. For determining the * main memory size of the given object only static attributes are excluded * from the memory measurement. * * @param object the object the memory size is to be determined. * @param referenceSize the memory size of a reference. * @param processedObjects a set containing the traversed attributes * (required for cycle detection). * @param excludedClasses a set of classes which are excluded during memory * measurement. * @param excludedFields a set of fields which are excluded during memory * measurement. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int memSizeOf(Object object, int referenceSize, Set<? super Wrapper> processedObjects, Set<Class<?>> excludedClasses, Set<Field> excludedFields) throws IllegalAccessException { return sizeOf(object, referenceSize, processedObjects, excludedClasses, excludedFields, Modifier.STATIC); } /** * The main memory size of the given object is computed recursively, by * determining the attributes of the class the object is an instance of. * The size of references on different platforms (i.e. 32-bit and 64-bit * platforms respectively) can be specified separately. For determining the * main memory size of the given object only static attributes are excluded * from the memory measurement. * * @param object the object the memory size is to be determined. * @param referenceSize the memory size of a reference. * @param processedObjects a set containing the traversed attributes * (required for cycle detection). * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ @SuppressWarnings("unchecked") public static int memSizeOf(Object object, int referenceSize, Set<? super Wrapper> processedObjects) throws IllegalAccessException { return memSizeOf(object, referenceSize, processedObjects, Collections.EMPTY_SET, Collections.EMPTY_SET); } /** * The main memory size of the given object is computed recursively, by * determining the attributes of the class the object is an instance of. * The size of references on different platforms (i.e. 32-bit and 64-bit * platforms respectively) can be specified separately. For determining the * main memory size of the given object only static attributes are excluded * from the memory measurement. * * @param object the object the memory size is to be determined. * @param referenceSize the memory size of a reference. * @param excludedClasses a set of classes which are excluded during memory * measurement. * @param excludedFields a set of fields which are excluded during memory * measurement. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int memSizeOf(Object object, int referenceSize, Set<Class<?>> excludedClasses, Set<Field> excludedFields) throws IllegalAccessException { return memSizeOf(object, referenceSize, new HashSet<Wrapper>(), excludedClasses, excludedFields); } /** * The main memory size of the given object is computed recursively, by * determining the attributes of the class the object is an instance of. * The size of references on different platforms (i.e. 32-bit and 64-bit * platforms respectively) can be specified separately. For determining the * main memory size of the given object only static attributes are excluded * from the memory measurement. * * @param object the object the memory size is to be determined. * @param referenceSize the memory size of a reference. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int memSizeOf(Object object, int referenceSize) throws IllegalAccessException { return memSizeOf(object, referenceSize, new HashSet<Wrapper>()); } /** * The main memory size of the given object is computed recursively, by * determining the attributes of the class the object is an instance of. * The size is computed for a 32-bit platform, i.e. a reference allocates * 8 bytes of main memory. For determining the main memory size of the * given object only static attributes are excluded from the memory * measurement. * * @param object the object the memory size is to be determined. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int memSizeOf(Object object) throws IllegalAccessException { return memSizeOf(object, 4); } /** * This method computes the (secondary) memory size of the given object. To * get the memory size of the specified object, a reference to this object * is needed. If this reference is <code>null</code>, the trivial case, the * returned object size is 0 bytes. Otherwise * {@link #sizeOf(Object, int, Set, Set, Set, int)} is called, which * analyzes the class this object is an instance of, the super classes and * especially the memory size allocated for each non-static and * non-transient attribute of these classes is determined and saved in a * set. For further information of saving the objects in the set see * {@link Wrapper}. * * @see #sizeOf(Object, int, Set, Set, Set, int) * * @param object the object the memory size is to be determined. * @param processedObjects a set containing the traversed attributes * (required for cycle detection). * @param excludedClasses a set of classes which are excluded during memory * measurement. * @param excludedFields a set of fields which are excluded during memory * measurement. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int getObjectSize(Object object, Set<? super Wrapper> processedObjects, Set<Class<?>> excludedClasses, Set<Field> excludedFields) throws IllegalAccessException { return sizeOf(object, REFERENCE_MEM_SIZE, processedObjects, excludedClasses, excludedFields, Modifier.STATIC, Modifier.TRANSIENT); } /** * This method computes the (secondary) memory size of the given object. To * get the memory size of the specified object, a reference to this object * is needed. If this reference is <code>null</code>, the trivial case, the * returned object size is 0 bytes. Otherwise * {@link #sizeOf(Object, int, Set, Set, Set, int)} is called, which * analyzes the class this object is an instance of, the super classes and * especially the memory size allocated for each non-static and * non-transient attribute of these classes is determined and saved in a * set. For further information of saving the objects in the set see * {@link Wrapper}. * * @see #sizeOf(Object, int, Set, Set, Set, int) * * @param object the object the memory size is to be determined. * @param processedObjects a set containing the traversed attributes * (required for cycle detection). * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ @SuppressWarnings("unchecked") public static int getObjectSize(Object object, Set<? super Wrapper> processedObjects) throws IllegalAccessException { return getObjectSize(object, processedObjects, Collections.EMPTY_SET, Collections.EMPTY_SET); } /** * This method computes the (secondary) memory size of the given object. To * get the memory size of the specified object, a reference to this object * is needed. If this reference is <code>null</code>, the trivial case, the * returned object size is 0 bytes. Otherwise * {@link #sizeOf(Object, int, Set, Set, Set, int)} is called, which * analyzes the class this object is an instance of, the super classes and * especially the memory size allocated for each non-static and * non-transient attribute of these classes is determined and saved in a * set. For further information of saving the objects in the set see * {@link Wrapper}. * * @see #sizeOf(Object, int, Set, Set, Set, int) * * @param object the object the memory size is to be determined. * @param excludedClasses a set of classes which are excluded during memory * measurement. * @param excludedFields a set of fields which are excluded during memory * measurement. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int getObjectSize(Object object, Set<Class<?>> excludedClasses, Set<Field> excludedFields) throws IllegalAccessException { return getObjectSize(object, new HashSet<Wrapper>(), excludedClasses, excludedFields); } /** * This method computes the (secondary) memory size of the given object. To * get the memory size of the specified object, a reference to this object * is needed. If this reference is <code>null</code>, the trivial case, the * returned object size is 0 bytes. Otherwise * {@link #sizeOf(Object, int, Set, Set, Set, int)} is called, which * analyzes the class this object is an instance of, the super classes and * especially the memory size allocated for each non-static and * non-transient attribute of these classes is determined and saved in a * set. For further information of saving the objects in the set see * {@link Wrapper}. * * @see #sizeOf(Object, int, Set, Set, Set, int) * * @param object the object the memory size is to be determined. * @return the memory size of the specified object. * @throws IllegalAccessException if a field of the given object cannot be * accessed. */ public static int getObjectSize(Object object) throws IllegalAccessException { return getObjectSize(object, new HashSet<Wrapper>()); } /** * Serializes an object and returns its byte representation. * * @param o input object to be serialized. * @return byte array containing the byte representation. */ public static byte[] serializeObject(Serializable o) { try { ByteArrayOutputStream output = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(output); out.writeObject(o); out.flush(); out.close(); return output.toByteArray(); } catch (IOException e) { throw new WrappingRuntimeException(e); } } /** * Deserializes a byte array to an object and returns it. * @param b byte array containing the byte representation of an object. * @return deserialized object. */ public static Object deserializeObject(byte b[]) { try { ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(b)); Object o = in.readObject(); in.close(); return o; } catch (IOException e) { throw new WrappingRuntimeException(e); } catch (ClassNotFoundException e) { throw new WrappingRuntimeException(e); } } /** * Converts an object using the given converter and returns its byte * representation. * * @param <T> the type of the object to be converted. * @param o input object to be serialized. * @param converter the converter used for converting the object. * @return byte array containing the byte representation. */ public static <T> byte[] convertObject(T o, Converter<? super T> converter) { try { ByteArrayOutputStream output = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(output); converter.write(out, o); out.flush(); out.close(); return output.toByteArray(); } catch (IOException e) { throw new WrappingRuntimeException(e); } } /** * Reconverts a byte array to an object using the given converter and * returns it. * * @param <T> the type of the object to be reconverted. * @param b byte array containing the byte representation of an object. * @param converter the converter used for reconverting the object. * @return reconverted object. */ public static <T> T reconvertObject(byte b[], Converter<T> converter) { try { DataInputStream in = new DataInputStream(new ByteArrayInputStream(b)); T o = converter.read(in); in.close(); return o; } catch (IOException e) { throw new WrappingRuntimeException(e); } } /** * Clones an object whether or not the clone method is protected (via reflection). * If a clone is not possible, then a RuntimeException is thrown. * @param o to be cloned. * @return the cloned Object. */ public static Object cloneObject(Object o) { try { Method m = o.getClass().getMethod("clone"); m.setAccessible(true); return m.invoke(o); } catch (Exception e) { throw new WrappingRuntimeException(e); } } /** * Determines iff the mainmaker called the current class. * @return true iff the mainmaker called the current class. */ public static boolean calledFromMainMaker() { try { throw new RuntimeException(); } catch (Exception e) { StackTraceElement st[] = e.getStackTrace(); for (int i=0; i<st.length; i++) if (st[i].getClassName().toLowerCase().indexOf("mainmaker")>=0) return true; return false; } } /** * Returns the outpath that has been passed to java via the -D Option. * @return String - the outpath */ public static String getOutPath() { String s = System.getProperty("xxloutpath"); if (s==null) throw new RuntimeException("xxloutpath has not been given as a parameter. Use java -Dxxloutpath=..."); return s; } /** * Returns the rootpath that has been passed to java via the -D Option. * @return String - the outpath */ public static String getRootPath() { String s = System.getProperty("xxlrootpath"); if (s==null) throw new RuntimeException("xxlrootpath has not been given as a parameter. Use java -Dxxlrootpath=..."); return s; } /** * Constructs a directory inside the outpath with the desired subdirectory. * The partial names of the subdirectory have to be passed inside the subdirs * array. * The path does not have a file separator at the end. * @param subdirs partial names of the path * @return whole path (contains a file separator at the end). */ public static String getOutPath(String subdirs[]) { StringBuffer sb = new StringBuffer(XXLSystem.getOutPath()); for (int i=0; i<subdirs.length; i++) sb.append(File.separator + subdirs[i]); String s = sb.toString(); File f = new File(s); f.mkdirs(); return s; } /** * Returns a data path inside xxl/data, which points to * a certain subdirectory. * The partial names of the subdirectory have to be passed inside the subdirs * array. * The path does not have a file separator at the end. * * @param subdirs string containing subpath * @return whole path (contains a file separator at the end). */ public static String getDataPath(String subdirs[]) { StringBuffer sb = new StringBuffer(XXLSystem.getRootPath()); sb.append(File.separator + "data"); for (int i=0; i<subdirs.length; i++) sb.append(File.separator + subdirs[i]); return sb.toString(); } /** * Returns the version of the Java RTE (only the major version * number). * * @return returns the Java RTE major version number */ public static double getJavaVersion() { String s = System.getProperty("java.version"); int pos = s.indexOf('.'); if (pos>=0) { pos = s.indexOf('.',pos+1); if (pos>0) s = s.substring(0,pos); } return Double.parseDouble(s); } /** * Tries to get a value from the meta data of given object, specified by the key. * If the object does not implement the CompositeMetaData interface or the key * is not found, null is returned. * @see xxl.core.util.metaData.CompositeMetaData * * @param <I> the type o the key. * @param key the specified key * @param object an object * @return the value specified by the key. */ @SuppressWarnings("unchecked") public static <I> Object getValueFromMD (I key, Object object) { if (object == null || !(object instanceof CompositeMetaData)) return null; try { return ((CompositeMetaData<? super I, ? extends Object>)object).get(key); } catch (MetaDataException e) { return null; } } }