/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.make; import com.caucho.util.Crc64; import com.caucho.vfs.PersistentDependency; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Comparator; import java.util.logging.Level; import java.util.logging.Logger; /** * Representing a class that might change. */ public class ClassDependency implements PersistentDependency { private final static Logger log = Logger.getLogger(ClassDependency.class.getName()); private Class _cl; private String _className; private boolean _checkFields = true; private boolean _checkStatic = true; private boolean _checkProtected = true; private boolean _checkPrivate = true; private boolean _isDigestModified; private long _newDigest; /** * Creates the class dependency. */ public ClassDependency(Class cl) { _cl = cl; _className = cl.getName(); } /** * Create a new dependency with a given digest. * * @param cl the source class * @param digest the MD5 digest */ public ClassDependency(String className, long digest) { _className = className; try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); _cl = Class.forName(className, false, loader); } catch (ClassNotFoundException e) { log.log(Level.FINE, e.toString(), e); } long newDigest = getDigest(); if (newDigest != digest) { if (log.isLoggable(Level.FINE)) log.fine(className + " class digest is modified (old=" + digest + ",new=" + newDigest + ")"); _isDigestModified = true; } } /** * Returns true if the underlying resource has changed. */ public boolean isModified() { return _isDigestModified; } /** * Returns true if the underlying resource has changed. */ public boolean logModified(Logger log) { if (isModified()) { log.info(_className + " digest is modified"); return true; } else return false; } /** * Calculates a MD5 digest of the class. */ public long getDigest() { try { if (_newDigest != 0) return _newDigest; if (_cl == null) return -1; long digest = 37; digest = addDigest(digest, _cl); _newDigest = digest; } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); _newDigest = -1; } return _newDigest; } /** * Calculates a MD5 digest of the class. */ private long addDigest(long digest, Class cl) throws Exception { if (_cl == null) return digest; digest = addDigest(digest, cl.getName()); digest = addDigest(digest, cl.getModifiers()); Class superClass = cl.getSuperclass(); if (superClass != null && superClass.getName().startsWith("java.") && ! superClass.getName().startsWith("javax.")) { digest = addDigest(digest, superClass); } Class []interfaces = cl.getInterfaces(); Arrays.sort(interfaces, ClassComparator.CMP); for (int i = 0; i < interfaces.length; i++) digest = addDigest(digest, interfaces[i].getName()); if (_checkFields) { Field []fields = cl.getDeclaredFields(); Arrays.sort(fields, FieldComparator.CMP); for (int i = 0; i < fields.length; i++) { int modifiers = fields[i].getModifiers(); if (Modifier.isPrivate(modifiers) && ! _checkPrivate) continue; if (Modifier.isProtected(modifiers) && ! _checkProtected) continue; digest = addDigest(digest, fields[i].getName()); digest = addDigest(digest, fields[i].getModifiers()); digest = addDigest(digest, fields[i].getType().getName()); // jpa/0021 Annotation[] annotations = fields[i].getAnnotations(); for (Annotation annotation : annotations) { digest = addDigest(digest, annotation.annotationType().getName()); } } } Method []methods = cl.getDeclaredMethods(); Arrays.sort(methods, MethodComparator.CMP); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; int modifiers = method.getModifiers(); if (Modifier.isPrivate(modifiers) && ! _checkPrivate) continue; if (Modifier.isProtected(modifiers) && ! _checkProtected) continue; if (Modifier.isStatic(modifiers) && ! _checkStatic) continue; digest = addDigest(digest, method.getName()); digest = addDigest(digest, method.getModifiers()); digest = addDigest(digest, method.getName()); Class []param = method.getParameterTypes(); for (int j = 0; j < param.length; j++) digest = addDigest(digest, param[j].getName()); digest = addDigest(digest, method.getReturnType().getName()); Class []exn = method.getExceptionTypes(); Arrays.sort(exn, ClassComparator.CMP); for (int j = 0; j < exn.length; j++) digest = addDigest(digest, exn[j].getName()); } return digest; } /** * Returns a string which will recreate the dependency. */ public String getJavaCreateString() { return ("new com.caucho.make.ClassDependency(" + "\"" + _className.replace('$', '.') + "\"" + ", " + getDigest() + "L)"); } /** * Adds the int to the digest. */ private static long addDigest(long digest, long v) { digest = Crc64.generate(digest, (byte) (v >> 24)); digest = Crc64.generate(digest, (byte) (v >> 16)); digest = Crc64.generate(digest, (byte) (v >> 8)); digest = Crc64.generate(digest, (byte) v); return digest; } /** * Adds the string to the digest using a UTF8 encoding. */ private static long addDigest(long digest, String string) { return Crc64.generate(digest, string); } public int hashCode() { return _className.hashCode(); } public boolean equals(Object o) { if (o == this) return true; if (! (o instanceof ClassDependency)) return false; ClassDependency depend = (ClassDependency) o; return _className.equals(depend._className); } static class ClassComparator implements Comparator<Class> { static final ClassComparator CMP = new ClassComparator(); public int compare(Class a, Class b) { if (a == b) return 0; else if (a == null) return -1; else if (b == null) return 1; return a.getName().compareTo(b.getName()); } } static class FieldComparator implements Comparator<Field> { static final FieldComparator CMP = new FieldComparator(); public int compare(Field a, Field b) { if (a == b) return 0; else if (a == null) return -1; else if (b == null) return 1; int cmp = a.getName().compareTo(b.getName()); if (cmp != 0) return cmp; cmp = a.getDeclaringClass().getName().compareTo(b.getDeclaringClass().getName()); if (cmp != 0) return cmp; return a.getType().getName().compareTo(b.getType().getName()); } } static class MethodComparator implements Comparator<Method> { static final MethodComparator CMP = new MethodComparator(); public int compare(Method a, Method b) { if (a == b) return 0; else if (a == null) return -1; else if (b == null) return 1; int cmp = a.getName().compareTo(b.getName()); if (cmp != 0) return cmp; Class []paramA = a.getParameterTypes(); Class []paramB = b.getParameterTypes(); if (paramA.length < paramB.length) return -1; else if (paramB.length < paramA.length) return 1; for (int i = 0; i < paramA.length; i++) { cmp = paramA[i].getName().compareTo(paramB[i].getName()); if (cmp != 0) return cmp; } cmp = a.getDeclaringClass().getName().compareTo(b.getDeclaringClass().getName()); if (cmp != 0) return cmp; return a.getReturnType().getName().compareTo(b.getReturnType().getName()); } } public String toString() { return getClass().getSimpleName() + "[" + _className + "]"; } }