KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > nbbuild > VerifyClassLinkage


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.nbbuild;
21
22 import java.io.ByteArrayInputStream JavaDoc;
23 import java.io.ByteArrayOutputStream JavaDoc;
24 import java.io.DataInput JavaDoc;
25 import java.io.DataInputStream JavaDoc;
26 import java.io.File JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.util.Arrays JavaDoc;
30 import java.util.Enumeration JavaDoc;
31 import java.util.HashMap JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.TreeMap JavaDoc;
36 import java.util.jar.Attributes JavaDoc;
37 import java.util.jar.JarEntry JavaDoc;
38 import java.util.jar.JarFile JavaDoc;
39 import java.util.jar.Manifest JavaDoc;
40 import java.util.regex.Pattern JavaDoc;
41 import org.apache.tools.ant.AntClassLoader;
42 import org.apache.tools.ant.BuildException;
43 import org.apache.tools.ant.Project;
44 import org.apache.tools.ant.Task;
45 import org.apache.tools.ant.types.Path;
46
47 /**
48  * Verifies linkage between classes in a JAR (typically a module).
49  * @author Jesse Glick
50  * @see "#71675"
51  * @see <a HREF="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html">Class file spec</a>
52  */

53 public class VerifyClassLinkage extends Task {
54     
55     public VerifyClassLinkage() {}
56     
57     /*
58     private boolean verifyMainJar = true;
59     private boolean verifyClassPathExtensions = true;
60     public void setVerifyClassPathExtensions(boolean verifyClassPathExtensions) {
61         this.verifyClassPathExtensions = verifyClassPathExtensions;
62     }
63     public void setVerifyMainJar(boolean verifyMainJar) {
64         this.verifyMainJar = verifyMainJar;
65     }
66      */

67
68     private File JavaDoc jar;
69     private boolean failOnError = true;
70     private boolean warnOnDefaultPackage = true;
71     private Path classpath = new Path(getProject());
72     private String JavaDoc ignores;
73
74     /**
75      * Intended static classpath for this JAR.
76      * Any classes loaded in this JAR (and its Class-Path extensions)
77      * must be linkable against this classpath plus the JAR (and extensions) itself.
78      */

79     public Path createClasspath() {
80         return classpath.createPath();
81     }
82
83     /**
84      * Specify the main JAR file.
85      * Automatically searches in Class-Path extensions too.
86      */

87     public void setJar(File JavaDoc jar) {
88         this.jar = jar;
89     }
90
91     /**
92      * If true (default), halt build on error, rather than just
93      * reporting a warning.
94      */

95     public void setFailOnError(boolean failOnError) {
96         this.failOnError = failOnError;
97     }
98     
99     /**
100      * Sets the pattern for classes that are not verified.
101      * Allows to skip linkage verification of some classes.
102      */

103     public void setIgnores(String JavaDoc ignores) {
104         this.ignores = ignores;
105     }
106
107     /**
108      * If true (default), warn if any classes are found in the default
109      * package. Never halts the build even if {@link #setFailOnError} true.
110      */

111     public void setWarnOnDefaultPackage(boolean warnOnDefaultPackage) {
112         this.warnOnDefaultPackage = warnOnDefaultPackage;
113     }
114
115     public void execute() throws BuildException {
116         if (jar == null) {
117             throw new BuildException("Must specify a JAR file", getLocation());
118         }
119         try {
120             // Map from class name (foo/Bar format) to true (found), false (not found), null (as yet unknown):
121
Map JavaDoc<String JavaDoc,Boolean JavaDoc> loadable = new HashMap JavaDoc<String JavaDoc,Boolean JavaDoc>();
122             Map JavaDoc<String JavaDoc,byte[]> classfiles = new TreeMap JavaDoc<String JavaDoc,byte[]>();
123             read(jar, classfiles, new HashSet JavaDoc<File JavaDoc>());
124             for (String JavaDoc clazz: classfiles.keySet()) {
125                 // All classes we define are obviously loadable:
126
loadable.put(clazz, Boolean.TRUE);
127                 if (warnOnDefaultPackage && clazz.indexOf('.') == -1) {
128                     log("Warning: class '" + clazz + "' found in default package", Project.MSG_WARN);
129                 }
130             }
131             ClassLoader JavaDoc loader = new AntClassLoader(ClassLoader.getSystemClassLoader().getParent(), getProject(), classpath, true);
132             for (Map.Entry JavaDoc<String JavaDoc, byte[]> entry: classfiles.entrySet()) {
133                 String JavaDoc clazz = entry.getKey();
134                 byte[] data = entry.getValue();
135                 verify(clazz, data, loadable, loader);
136             }
137         } catch (IOException JavaDoc e) {
138             throw new BuildException("While verifying " + jar + " or its Class-Path extensions: " + e, e, getLocation());
139         }
140     }
141
142     private void read(File JavaDoc jar, Map JavaDoc<String JavaDoc,byte[]> classfiles, Set JavaDoc<File JavaDoc> alreadyRead) throws IOException JavaDoc {
143         if (!alreadyRead.add(jar)) {
144             log("Already read " + jar, Project.MSG_VERBOSE);
145             return;
146         }
147         log("Reading " + jar, Project.MSG_VERBOSE);
148         JarFile JavaDoc jf = new JarFile JavaDoc(jar);
149         Pattern JavaDoc p = (ignores != null)? Pattern.compile(ignores): null;
150         try {
151             Enumeration JavaDoc e = jf.entries();
152             while (e.hasMoreElements()) {
153                 JarEntry JavaDoc entry = (JarEntry JavaDoc) e.nextElement();
154                 String JavaDoc name = entry.getName();
155                 if (!name.endsWith(".class")) {
156                     continue;
157                 }
158                 String JavaDoc clazz = name.substring(0, name.length() - 6).replace('/', '.');
159                 if (p != null && p.matcher(clazz).matches()) {
160                     continue;
161                 }
162                 ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc(Math.max((int) entry.getSize(), 0));
163                 InputStream JavaDoc is = jf.getInputStream(entry);
164                 try {
165                     byte[] buf = new byte[4096];
166                     int read;
167                     while ((read = is.read(buf)) != -1) {
168                         baos.write(buf, 0, read);
169                     }
170                 } finally {
171                     is.close();
172                 }
173                 classfiles.put(clazz, baos.toByteArray());
174             }
175             Manifest JavaDoc mf = jf.getManifest();
176             if (mf != null) {
177                 String JavaDoc cp = mf.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
178                 if (cp != null) {
179                     String JavaDoc[] uris = cp.trim().split("[, ]+");
180                     for (int i = 0; i < uris.length; i++) {
181                         File JavaDoc otherJar = new File JavaDoc(jar.toURI().resolve(uris[i]));
182                         if (otherJar.isFile()) {
183                             read(otherJar, classfiles, alreadyRead);
184                         }
185                     }
186                 }
187             }
188         } finally {
189             jf.close();
190         }
191     }
192
193     private static void skip(DataInput JavaDoc input, int bytes) throws IOException JavaDoc {
194         int skipped = input.skipBytes(bytes);
195         if (skipped != bytes) {
196             throw new IOException JavaDoc("Truncated class file");
197         }
198     }
199     private void verify(String JavaDoc clazz, byte[] data, Map JavaDoc<String JavaDoc,Boolean JavaDoc> loadable, ClassLoader JavaDoc loader) throws IOException JavaDoc, BuildException {
200         //log("Verifying linkage of " + clazz.replace('/', '.'), Project.MSG_DEBUG);
201
DataInput JavaDoc input = new DataInputStream JavaDoc(new ByteArrayInputStream JavaDoc(data));
202         skip(input, 8); // magic, minor_version, major_version
203
int size = input.readUnsignedShort() - 1; // constantPoolCount
204
String JavaDoc[] utf8Strings = new String JavaDoc[size];
205         boolean[] isClassName = new boolean[size];
206         for (int i = 0; i < size; i++) {
207             byte tag = input.readByte();
208             switch (tag) {
209                 case 1: // CONSTANT_Utf8
210
utf8Strings[i] = input.readUTF();
211                     break;
212                 case 7: // CONSTANT_Class
213
int index = input.readUnsignedShort() - 1;
214                     if (index >= size) {
215                         throw new IOException JavaDoc("CONSTANT_Class index " + index + " too big for size of pool " + size);
216                     }
217                     //log("Class reference at " + index, Project.MSG_DEBUG);
218
isClassName[index] = true;
219                     break;
220                 case 3: // CONSTANT_Integer
221
case 4: // CONSTANT_Float
222
case 9: // CONSTANT_Fieldref
223
case 10: // CONSTANT_Methodref
224
case 11: // CONSTANT_InterfaceMethodref
225
case 12: // CONSTANT_NameAndType
226
skip(input, 4);
227                     break;
228                 case 8: // CONSTANT_String
229
skip(input, 2);
230                     break;
231                 case 5: // CONSTANT_Long
232
case 6: // CONSTANT_Double
233
skip(input, 8);
234                     i++; // weirdness in spec
235
break;
236                 default:
237                     throw new IOException JavaDoc("Unrecognized constant pool tag " + tag + " at index " + i + "; running UTF-8 strings: " + Arrays.asList(utf8Strings));
238             }
239         }
240         log("UTF-8 strings: " + Arrays.asList(utf8Strings), Project.MSG_DEBUG);
241         for (int i = 0; i < size; i++) {
242             if (!isClassName[i]) {
243                 continue;
244             }
245             String JavaDoc vmname = utf8Strings[i];
246             while (vmname.charAt(0) == '[') {
247                 // array type
248
vmname = vmname.substring(1);
249             }
250             if (vmname.length() == 1) {
251                 // primitive
252
continue;
253             }
254             String JavaDoc clazz2;
255             if (vmname.charAt(vmname.length() - 1) == ';' && vmname.charAt(0) == 'L') {
256                 // Uncommon but seems sometimes this happens.
257
clazz2 = vmname.substring(1, vmname.length() - 1);
258             } else {
259                 clazz2 = vmname;
260             }
261             Boolean JavaDoc exists = loadable.get(clazz2.replace('/', '.'));
262             if (exists == null) {
263                 exists = Boolean.valueOf(loader.getResource(clazz2 + ".class") != null);
264                 loadable.put(clazz2, exists);
265             }
266             if (!exists.booleanValue()) {
267                 String JavaDoc message = clazz + " cannot access " + clazz2.replace('/', '.');
268                 if (failOnError) {
269                     throw new BuildException(message, getLocation());
270                 } else {
271                     log("Warning: " + message, Project.MSG_WARN);
272                 }
273             } else {
274                 //log("Working reference to " + clazz2.replace('/', '.'), Project.MSG_DEBUG);
275
}
276         }
277     }
278     
279 }
280
Popular Tags