| /* |
| * 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.directmemory.lightning.internal.util; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectStreamClass; |
| import java.io.Serializable; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import org.apache.directmemory.lightning.metadata.ClassDefinition; |
| import org.objectweb.asm.AnnotationVisitor; |
| import org.objectweb.asm.Attribute; |
| import org.objectweb.asm.ClassReader; |
| import org.objectweb.asm.ClassVisitor; |
| import org.objectweb.asm.FieldVisitor; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| |
| public final class ClassUtil |
| { |
| |
| public static final ClassDefinition[] CLASS_DESCRIPTORS = new ClassDefinition[] { |
| new JavaBuildInTypeClassDefinition( boolean.class, 1 ), new JavaBuildInTypeClassDefinition( Boolean.class, 2 ), |
| new JavaBuildInTypeClassDefinition( byte.class, 3 ), new JavaBuildInTypeClassDefinition( Byte.class, 4 ), |
| new JavaBuildInTypeClassDefinition( char.class, 5 ), new JavaBuildInTypeClassDefinition( Character.class, 6 ), |
| new JavaBuildInTypeClassDefinition( double.class, 7 ), new JavaBuildInTypeClassDefinition( Double.class, 8 ), |
| new JavaBuildInTypeClassDefinition( float.class, 9 ), new JavaBuildInTypeClassDefinition( Float.class, 10 ), |
| new JavaBuildInTypeClassDefinition( int.class, 11 ), new JavaBuildInTypeClassDefinition( Integer.class, 12 ), |
| new JavaBuildInTypeClassDefinition( long.class, 13 ), new JavaBuildInTypeClassDefinition( Long.class, 14 ), |
| new JavaBuildInTypeClassDefinition( short.class, 15 ), new JavaBuildInTypeClassDefinition( Short.class, 16 ), |
| new JavaBuildInTypeClassDefinition( String.class, 17 ), new JavaBuildInTypeClassDefinition( List.class, 18 ), |
| new JavaBuildInTypeClassDefinition( Set.class, 19 ), new JavaBuildInTypeClassDefinition( Map.class, 20 ), |
| new JavaBuildInTypeClassDefinition( BigInteger.class, 21 ), |
| new JavaBuildInTypeClassDefinition( BigDecimal.class, 22 ) }; |
| |
| private static final Map<Class<?>, Long> SERIAL_VERSION_UID_CACHE = new ConcurrentHashMap<Class<?>, Long>(); |
| |
| private ClassUtil() |
| { |
| } |
| |
| public static boolean isReferenceCapable( Class<?> type ) |
| { |
| return !type.isPrimitive() && Boolean.class != type && Byte.class != type && Short.class != type |
| && Integer.class != type && Long.class != type && Float.class != type && Double.class != type; |
| } |
| |
| public static Class<?> loadClass( String canonicalName ) |
| throws ClassNotFoundException |
| { |
| return loadClass( canonicalName, ClassUtil.class.getClassLoader() ); |
| } |
| |
| public static Class<?> loadClass( String canonicalName, ClassLoader classLoader ) |
| throws ClassNotFoundException |
| { |
| Class<?> type = null; |
| try |
| { |
| type = classLoader.loadClass( canonicalName ); |
| } |
| catch ( ClassNotFoundException e ) |
| { |
| // Intentionally left blank |
| } |
| |
| if ( type == null ) |
| { |
| try |
| { |
| type = Class.forName( canonicalName ); |
| } |
| catch ( ClassNotFoundException e ) |
| { |
| // Intentionally left blank |
| } |
| } |
| |
| if ( type == null ) |
| { |
| try |
| { |
| ClassLoader tcl = Thread.currentThread().getContextClassLoader(); |
| type = tcl.loadClass( canonicalName ); |
| } |
| catch ( ClassNotFoundException e ) |
| { |
| // Intentionally left blank |
| } |
| } |
| |
| if ( type == null ) |
| { |
| try |
| { |
| ClassLoader ccl = ClassUtil.class.getClassLoader(); |
| type = ccl.loadClass( canonicalName ); |
| } |
| catch ( ClassNotFoundException e ) |
| { |
| // Intentionally left blank |
| } |
| } |
| |
| if ( type != null ) |
| { |
| return type; |
| } |
| |
| throw new ClassNotFoundException( "Class " + canonicalName + " not found on classpath" ); |
| } |
| |
| public static long calculateSerialVersionUID( Class<?> clazz ) |
| { |
| Long serialVersion = SERIAL_VERSION_UID_CACHE.get( clazz ); |
| if ( serialVersion != null ) |
| { |
| return serialVersion; |
| } |
| |
| if ( Serializable.class.isAssignableFrom( clazz ) ) |
| { |
| serialVersion = ObjectStreamClass.lookup( clazz ).getSerialVersionUID(); |
| SERIAL_VERSION_UID_CACHE.put( clazz, serialVersion ); |
| return serialVersion; |
| } |
| |
| serialVersion = getSerialVersionUIDFromField( clazz ); |
| if ( serialVersion != null ) |
| { |
| SERIAL_VERSION_UID_CACHE.put( clazz, serialVersion ); |
| return serialVersion; |
| } |
| |
| try |
| { |
| ClassReader reader = new ClassReader( Type.getInternalName( clazz ).replace( "/", "." ) ); |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| DataOutputStream out = new DataOutputStream( baos ); |
| |
| SerialVersionClassVisitor classVisitor = new SerialVersionClassVisitor(); |
| reader.accept( classVisitor, 0 ); |
| |
| // Classname |
| out.writeUTF( toJavaName( classVisitor.name ) ); |
| |
| // Modifiers |
| out.writeInt( clazz.getModifiers() |
| & ( Modifier.PUBLIC | Modifier.FINAL | Modifier.INTERFACE | Modifier.ABSTRACT ) ); |
| |
| // Interfaces |
| Collections.sort( classVisitor.interfaces ); |
| for ( int i = 0; i < classVisitor.interfaces.size(); i++ ) |
| { |
| out.writeUTF( toJavaName( classVisitor.interfaces.get( i ) ) ); |
| } |
| |
| // Fields |
| Field[] fields = clazz.getDeclaredFields(); |
| Arrays.sort( fields, new Comparator<Field>() |
| { |
| |
| @Override |
| public int compare( Field o1, Field o2 ) |
| { |
| return o1.getName().compareTo( o2.getName() ); |
| } |
| } ); |
| |
| for ( Field field : fields ) |
| { |
| int mods = field.getModifiers(); |
| if ( ( ( mods & Modifier.PRIVATE ) == 0 || ( mods & ( Modifier.STATIC | Modifier.TRANSIENT ) ) == 0 ) ) |
| { |
| out.writeUTF( field.getName() ); |
| out.writeInt( mods ); |
| out.writeUTF( Type.getDescriptor( field.getType() ) ); |
| } |
| } |
| |
| // Static Initializer |
| if ( classVisitor.staticInitializerFound ) |
| { |
| out.writeUTF( "<clinit>" ); |
| out.writeInt( Modifier.STATIC ); |
| out.writeUTF( "()V" ); |
| } |
| |
| // Constructors |
| Constructor<?>[] constructors = clazz.getDeclaredConstructors(); |
| Arrays.sort( constructors, new Comparator<Constructor<?>>() |
| { |
| |
| @Override |
| public int compare( Constructor<?> o1, Constructor<?> o2 ) |
| { |
| return Type.getConstructorDescriptor( o1 ).compareTo( Type.getConstructorDescriptor( o2 ) ); |
| } |
| } ); |
| |
| for ( int i = 0; i < constructors.length; i++ ) |
| { |
| Constructor<?> constructor = constructors[i]; |
| int mods = constructor.getModifiers(); |
| if ( ( mods & Modifier.PRIVATE ) == 0 ) |
| { |
| out.writeUTF( "<init>" ); |
| out.writeInt( mods ); |
| out.writeUTF( toJavaName( Type.getConstructorDescriptor( constructor ) ) ); |
| } |
| } |
| |
| // Methods |
| Method[] methods = clazz.getDeclaredMethods(); |
| Arrays.sort( methods, new Comparator<Method>() |
| { |
| |
| @Override |
| public int compare( Method o1, Method o2 ) |
| { |
| return Type.getMethodDescriptor( o1 ).compareTo( Type.getMethodDescriptor( o2 ) ); |
| } |
| } ); |
| |
| for ( int i = 0; i < methods.length; i++ ) |
| { |
| Method method = methods[i]; |
| int mods = method.getModifiers(); |
| if ( ( mods & Modifier.PRIVATE ) == 0 ) |
| { |
| out.writeUTF( "<init>" ); |
| out.writeInt( mods ); |
| out.writeUTF( toJavaName( Type.getMethodDescriptor( method ) ) ); |
| } |
| } |
| |
| // Final calculation |
| out.flush(); |
| MessageDigest digest = MessageDigest.getInstance( "SHA" ); |
| byte[] checksum = digest.digest( baos.toByteArray() ); |
| |
| long hash = 0; |
| for ( int i = Math.min( checksum.length, 8 ) - 1; i >= 0; i-- ) |
| { |
| hash = ( hash << 8 ) | ( checksum[i] & 0xFF ); |
| } |
| |
| SERIAL_VERSION_UID_CACHE.put( clazz, hash ); |
| return hash; |
| } |
| catch ( IOException e ) |
| { |
| } |
| catch ( NoSuchAlgorithmException e ) |
| { |
| } |
| |
| return -1L; |
| } |
| |
| public static byte[] getClassBytes( Class<?> clazz ) |
| { |
| try |
| { |
| ClassLoader classLoader = clazz.getClassLoader(); |
| if ( classLoader == null ) |
| { |
| classLoader = Thread.currentThread().getContextClassLoader(); |
| } |
| |
| String internalName = Type.getInternalName( clazz ); |
| InputStream stream = classLoader.getResourceAsStream( internalName + ".class" ); |
| byte[] data = new byte[stream.available()]; |
| stream.read( data ); |
| stream.close(); |
| return data; |
| } |
| catch ( IOException e ) |
| { |
| throw new RuntimeException( "Class bytes could not be read", e ); |
| } |
| } |
| |
| private static String toJavaName( String classname ) |
| { |
| return classname.replace( "/", "." ); |
| } |
| |
| private static Long getSerialVersionUIDFromField( Class<?> clazz ) |
| { |
| try |
| { |
| Field f = clazz.getDeclaredField( "serialVersionUID" ); |
| int mask = Modifier.STATIC | Modifier.FINAL; |
| if ( ( f.getModifiers() & mask ) == mask ) |
| { |
| f.setAccessible( true ); |
| return Long.valueOf( f.getLong( null ) ); |
| } |
| } |
| catch ( Exception ex ) |
| { |
| } |
| return null; |
| } |
| |
| private static class JavaBuildInTypeClassDefinition |
| implements ClassDefinition |
| { |
| |
| private final long id; |
| |
| private final Class<?> type; |
| |
| private final String canonicalName; |
| |
| private final byte[] checksum = new byte[20]; |
| |
| private final long serialVersionUID = -1L; |
| |
| JavaBuildInTypeClassDefinition( Class<?> type, long id ) |
| { |
| this.id = id; |
| this.type = type; |
| this.canonicalName = type.getCanonicalName(); |
| } |
| |
| @Override |
| public String getCanonicalName() |
| { |
| return canonicalName; |
| } |
| |
| @Override |
| public Class<?> getType() |
| { |
| return type; |
| } |
| |
| @Override |
| public byte[] getChecksum() |
| { |
| return checksum; |
| } |
| |
| @Override |
| public long getId() |
| { |
| return id; |
| } |
| |
| @Override |
| public long getSerialVersionUID() |
| { |
| return serialVersionUID; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + ( ( canonicalName == null ) ? 0 : canonicalName.hashCode() ); |
| result = prime * result + Arrays.hashCode( checksum ); |
| result = prime * result + (int) ( id ^ ( id >>> 32 ) ); |
| result = prime * result + (int) ( serialVersionUID ^ ( serialVersionUID >>> 32 ) ); |
| return result; |
| } |
| |
| @Override |
| public boolean equals( Object obj ) |
| { |
| if ( this == obj ) |
| { |
| return true; |
| } |
| if ( obj == null ) |
| { |
| return false; |
| } |
| if ( getClass() != obj.getClass() ) |
| { |
| return false; |
| } |
| JavaBuildInTypeClassDefinition other = (JavaBuildInTypeClassDefinition) obj; |
| if ( canonicalName == null ) |
| { |
| if ( other.canonicalName != null ) |
| { |
| return false; |
| } |
| } |
| else if ( !canonicalName.equals( other.canonicalName ) ) |
| { |
| return false; |
| } |
| if ( !Arrays.equals( checksum, other.checksum ) ) |
| { |
| return false; |
| } |
| if ( id != other.id ) |
| { |
| return false; |
| } |
| if ( serialVersionUID != other.serialVersionUID ) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "JavaBuildInTypeClassDefinition [id=" + id + ", type=" + type + ", canonicalName=" + canonicalName |
| + ", checksum=" + Arrays.toString( checksum ) + ", serialVersionUID=" + serialVersionUID + "]"; |
| } |
| } |
| |
| private static class SerialVersionClassVisitor |
| extends ClassVisitor |
| { |
| |
| public SerialVersionClassVisitor() |
| { |
| super( Opcodes.ASM4 ); |
| } |
| |
| private List<String> interfaces = new ArrayList<String>(); |
| |
| private boolean staticInitializerFound = false; |
| |
| private String name; |
| |
| @Override |
| public void visit( int version, int access, String name, String signature, String superName, String[] interfaces ) |
| { |
| this.name = name; |
| this.interfaces = Arrays.asList( interfaces ); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation( String desc, boolean visible ) |
| { |
| return null; |
| } |
| |
| @Override |
| public void visitAttribute( Attribute attr ) |
| { |
| } |
| |
| @Override |
| public void visitEnd() |
| { |
| } |
| |
| @Override |
| public FieldVisitor visitField( int access, String name, String desc, String signature, Object value ) |
| { |
| return null; |
| } |
| |
| @Override |
| public void visitInnerClass( String name, String outerName, String innerName, int access ) |
| { |
| } |
| |
| @Override |
| public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions ) |
| { |
| if ( "<clinit>".equals( name ) && ( access & Opcodes.ACC_STATIC ) != 0 ) |
| { |
| staticInitializerFound = true; |
| } |
| return null; |
| } |
| |
| @Override |
| public void visitOuterClass( String owner, String name, String desc ) |
| { |
| } |
| |
| @Override |
| public void visitSource( String source, String debug ) |
| { |
| } |
| } |
| } |