View Javadoc
1 ////////////////////////////////////////////////////////////////////////////// 2 // Clirr: compares two versions of a java library for binary compatibility 3 // Copyright (C) 2003 - 2004 Lars Kühne 4 // 5 // This library is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 2.1 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 ////////////////////////////////////////////////////////////////////////////// 19 20 package net.sf.clirr; 21 22 import java.util.ArrayList; 23 import java.util.Iterator; 24 import java.util.List; 25 import java.util.Enumeration; 26 import java.util.zip.ZipFile; 27 import java.util.zip.ZipEntry; 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.net.URL; 32 import java.net.MalformedURLException; 33 import java.net.URLClassLoader; 34 35 import net.sf.clirr.checks.AddedClassCheck; 36 import net.sf.clirr.checks.ClassHierarchyCheck; 37 import net.sf.clirr.checks.ClassModifierCheck; 38 import net.sf.clirr.checks.GenderChangeCheck; 39 import net.sf.clirr.checks.InterfaceSetCheck; 40 import net.sf.clirr.checks.RemovedClassCheck; 41 import net.sf.clirr.checks.FieldSetCheck; 42 import net.sf.clirr.checks.MethodSetCheck; 43 import net.sf.clirr.event.ApiDifference; 44 import net.sf.clirr.event.DiffListener; 45 import net.sf.clirr.framework.ApiDiffDispatcher; 46 import net.sf.clirr.framework.ClassChangeCheck; 47 import net.sf.clirr.framework.ClassSetChangeCheck; 48 import org.apache.bcel.classfile.JavaClass; 49 import org.apache.bcel.classfile.ClassParser; 50 import org.apache.bcel.util.ClassSet; 51 import org.apache.bcel.util.Repository; 52 import org.apache.bcel.util.ClassLoaderRepository; 53 import org.apache.tools.ant.BuildException; 54 55 56 /*** 57 * This is the main class to be used by Clirr frontends, 58 * it implements the checking functionality of Clirr. 59 * Frontends can create an instance of this class 60 * and register themselves as DiffListeners, they are then 61 * informed whenever an API change is detected by the 62 * reportDiffs method. 63 * 64 * @author lkuehne 65 */ 66 public final class Checker implements ApiDiffDispatcher 67 { 68 69 private List listeners = new ArrayList(); 70 71 private List classSetChecks = new ArrayList(); 72 private List classChecks = new ArrayList(); 73 74 /*** 75 * Package visible constructor for unit testing. 76 */ 77 Checker(ClassSetChangeCheck cscc) 78 { 79 classSetChecks.add(cscc); 80 } 81 82 /*** 83 * Package visible constructor for unit testing. 84 */ 85 Checker(ClassChangeCheck ccc) 86 { 87 classChecks.add(ccc); 88 } 89 90 /*** 91 * Creates a new Checker. 92 */ 93 public Checker() 94 { 95 classSetChecks.add(new RemovedClassCheck(this)); 96 classSetChecks.add(new AddedClassCheck(this)); 97 98 classChecks.add(new GenderChangeCheck(this)); 99 classChecks.add(new ClassModifierCheck(this)); 100 classChecks.add(new InterfaceSetCheck(this)); 101 classChecks.add(new ClassHierarchyCheck(this)); 102 classChecks.add(new FieldSetCheck(this)); 103 classChecks.add(new MethodSetCheck(this)); 104 } 105 106 public void addDiffListener(DiffListener listener) 107 { 108 listeners.add(listener); 109 } 110 111 private void fireStart() 112 { 113 for (Iterator it = listeners.iterator(); it.hasNext();) 114 { 115 DiffListener diffListener = (DiffListener) it.next(); 116 diffListener.start(); 117 } 118 } 119 120 private void fireStop() 121 { 122 for (Iterator it = listeners.iterator(); it.hasNext();) 123 { 124 DiffListener diffListener = (DiffListener) it.next(); 125 diffListener.stop(); 126 } 127 } 128 129 public void fireDiff(ApiDifference diff) 130 { 131 for (Iterator it = listeners.iterator(); it.hasNext();) 132 { 133 DiffListener diffListener = (DiffListener) it.next(); 134 diffListener.reportDiff(diff); 135 } 136 } 137 138 public void reportDiffs( 139 File[] origJars, File[] newJars, 140 ClassLoader origThirdPartyLoader, ClassLoader newThirdPartyLoader) 141 { 142 final ClassSet origClasses = createClassSet(origJars, origThirdPartyLoader); 143 final ClassSet newClasses = createClassSet(newJars, newThirdPartyLoader); 144 reportDiffs(origClasses, newClasses); 145 } 146 147 private ClassSet createClassSet(File[] jarFiles, ClassLoader thirdPartyClasses) 148 { 149 ClassLoader classLoader = createClassLoader(jarFiles, thirdPartyClasses); 150 151 Repository repository = new ClassLoaderRepository(classLoader); 152 153 ClassSet ret = new ClassSet(); 154 155 for (int i = 0; i < jarFiles.length; i++) 156 { 157 File jarFile = jarFiles[i]; 158 ZipFile zip = null; 159 try 160 { 161 zip = new ZipFile(jarFile, ZipFile.OPEN_READ); 162 } 163 catch (IOException ex) 164 { 165 throw new BuildException("Cannot open " + jarFile + " for reading", ex); 166 } 167 Enumeration enum = zip.entries(); 168 while (enum.hasMoreElements()) 169 { 170 ZipEntry zipEntry = (ZipEntry) enum.nextElement(); 171 if (!zipEntry.isDirectory() && zipEntry.getName().endsWith(".class")) 172 { 173 JavaClass clazz = extractClass(zipEntry, zip, repository); 174 if (clazz.isPublic() || clazz.isProtected()) 175 { 176 ret.add(clazz); 177 } 178 } 179 } 180 } 181 182 return ret; 183 } 184 185 private JavaClass extractClass(ZipEntry zipEntry, ZipFile zip, Repository repository) 186 { 187 String name = zipEntry.getName(); 188 InputStream is = null; 189 try 190 { 191 is = zip.getInputStream(zipEntry); 192 193 ClassParser parser = new ClassParser(is, name); 194 JavaClass clazz = parser.parse(); 195 clazz.setRepository(repository); 196 return clazz; 197 } 198 catch (IOException ex) 199 { 200 throw new BuildException("Cannot read " + zipEntry.getName() + " from " + zip.getName(), ex); 201 } 202 finally 203 { 204 if (is != null) 205 { 206 try 207 { 208 is.close(); 209 } 210 catch (IOException ex) 211 { 212 throw new BuildException("Cannot close " + zip.getName(), ex); 213 } 214 } 215 } 216 } 217 218 private ClassLoader createClassLoader(File[] jarFiles, ClassLoader thirdPartyClasses) 219 { 220 final URL[] jarUrls = new URL[jarFiles.length]; 221 for (int i = 0; i < jarFiles.length; i++) 222 { 223 File jarFile = jarFiles[i]; 224 try 225 { 226 URL url = jarFile.toURL(); 227 jarUrls[i] = url; 228 } 229 catch (MalformedURLException ex) 230 { 231 final IllegalArgumentException illegalArgumentException = 232 new IllegalArgumentException("Cannot create classloader with jar file " + jarFile); 233 illegalArgumentException.initCause(ex); 234 throw illegalArgumentException; 235 } 236 } 237 final URLClassLoader jarsLoader = new URLClassLoader(jarUrls, thirdPartyClasses); 238 239 return jarsLoader; 240 } 241 242 243 /*** 244 * Checks two sets of classes for api changes and reports 245 * them to the DiffListeners. 246 * @param compatibilityBaseline the classes that form the 247 * compatibility baseline to check against 248 * @param currentVersion the classes that are checked for 249 * compatibility with compatibilityBaseline 250 */ 251 private void reportDiffs(ClassSet compatibilityBaseline, ClassSet currentVersion) 252 { 253 fireStart(); 254 for (Iterator it = classSetChecks.iterator(); it.hasNext();) 255 { 256 ClassSetChangeCheck check = (ClassSetChangeCheck) it.next(); 257 check.check(compatibilityBaseline, currentVersion); 258 } 259 runClassChecks(compatibilityBaseline, currentVersion); 260 fireStop(); 261 } 262 263 private void runClassChecks(ClassSet compatBaseline, ClassSet currentVersion) 264 { 265 JavaClass[] compat = compatBaseline.toArray(); 266 JavaClass[] current = currentVersion.toArray(); 267 268 for (int i = 0; i < compat.length; i++) 269 { 270 JavaClass compatBaselineClass = compat[i]; 271 JavaClass currentClass = findClass(compatBaselineClass.getClassName(), current); 272 if (currentClass != null) 273 { 274 // class still available in current release 275 for (Iterator it = classChecks.iterator(); it.hasNext();) 276 { 277 ClassChangeCheck classChangeCheck = (ClassChangeCheck) it.next(); 278 classChangeCheck.check(compatBaselineClass, currentClass); 279 } 280 } 281 } 282 } 283 284 private JavaClass findClass(String className, JavaClass[] javaClasses) 285 { 286 for (int i = 0; i < javaClasses.length; i++) 287 { 288 JavaClass javaClass = javaClasses[i]; 289 if (javaClass.getClassName().equals(className)) 290 { 291 return javaClass; 292 } 293 } 294 return null; 295 } 296 297 }

This page was automatically generated by Maven