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