001: /*
002:
003: Derby - Class org.apache.derbyBuild.classlister
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyBuild;
023:
024: import org.apache.derby.iapi.services.classfile.*;
025: import org.apache.derby.iapi.util.ByteArray;
026: import java.util.*;
027: import java.util.zip.*;
028: import java.io.*;
029:
030: import java.io.*;
031:
032: /**
033:
034: A tool that generates a list of required classes from a
035: set of properties files. The value of any property within a property
036: file that starts with 'derby.module.' is taken as a class name.
037: That class name and all the clases it requires are listed to
038: System.out, to facilitate building a zip file. Classes that
039: start with 'java.' or 'javax.' are not listed and are not
040: checked for dependent classes.
041: <P>
042: If the class name starts with 'com.ibm.db2j.' then a messages.properties
043: file is searched for corresponding to that class, if one exists then
044: is is added to the list of files printed.
045: <P>
046: The search path for the classes is $CLASSPATH
047: <P>
048: If the system property cloudscapeOnly is set to true then only classes
049: and message.properties files are listed that start with com.ibm.db2j.
050: <P>
051: The output for each class or properties file is a relative file
052: name that uses '/' as the file separator. e.g.
053:
054: com/ibm/db2j/core/Setup.class
055:
056: <P>
057: The output order of the classes & files is random.
058: <P>
059:
060:
061: Usage: java [-DignoreWebLogic=true] [-Dverbose=true] [-DcloudscapeOnly=true] [-DruntimeOnly=true]
062: [-Ddb2jtools=true]
063: [-DportingOnly=true] [-Doutputfile=<filename>] org.apache.derbyBuild.classlister
064: property_file [ property_file ... ]
065: **/
066:
067: public class classlister {
068:
069: protected String[] sets;
070: protected Hashtable foundClasses;
071: //protected ClassUtilitiesFactory cuf;
072:
073: protected boolean cloudscapeOnly = false;
074: protected boolean portingOnly = false;
075: protected boolean ignoreWebLogic = false;
076: protected boolean verbose = false;
077: protected boolean skipJava = true;
078: protected boolean skipJavax = true;
079: protected boolean skipOrg = true;
080: protected boolean skipInformix = true;
081: protected boolean skipDB2 = true;
082: protected boolean skipDB2e = true;
083: protected boolean skipSun = true;
084: protected boolean showAll = false;
085: protected boolean keepRolling = false;
086: protected boolean showOne = false;
087: protected Hashtable masterClassList = new Hashtable();
088: protected String classpath[] = null;
089: protected String outputfile;
090: protected Hashtable classpathHash;
091: protected int indent = 0;
092: protected int errorCount = 0;
093: protected PrintWriter pwOut;
094: protected PrintStream psOut;
095:
096: protected boolean db2jtools;
097: protected boolean db2jdrda;
098:
099: protected boolean keepDependencyHistory;
100:
101: protected static final String[] propFiles = {
102: "messages.properties", "instructions.properties",
103: "metadata.properties" };
104:
105: public static void main(String args[]) throws IOException {
106:
107: classlister me = new classlister();
108:
109: me.sets = args;
110:
111: me.run();
112: if (me.errorCount > 0) {
113: System.out.println(me.errorCount + " errors encountered.");
114: System.exit(1);
115: }
116: }
117:
118: public classlister() {
119: cloudscapeOnly = Boolean.getBoolean("cloudscapeOnly");
120: portingOnly = Boolean.getBoolean("portingOnly");
121: ignoreWebLogic = Boolean.getBoolean("ignoreWebLogic");
122: verbose = Boolean.getBoolean("verbose");
123: skipJava = !Boolean.getBoolean("doJava");
124: skipJavax = !Boolean.getBoolean("doJavax");
125: skipOrg = !Boolean.getBoolean("doOrg");
126: showAll = Boolean.getBoolean("showAll");
127: showOne = Boolean.getBoolean("showOne");
128: keepRolling = Boolean.getBoolean("keepRolling");
129: outputfile = System.getProperty("outputfile");
130: db2jtools = Boolean.getBoolean("db2jtools");
131: db2jdrda = Boolean.getBoolean("db2jdrda");
132:
133: keepDependencyHistory = showOne || showAll;
134: }
135:
136: public void run() {
137: //System.out.println("outputfile: " + outputfile);
138: try {
139: File outFile = new File(outputfile);
140: pwOut = new PrintWriter(new BufferedWriter(new FileWriter(
141: outFile.getPath()), 10000), true);
142: } catch (IOException ioe) {
143: System.out.println(ioe);
144: System.exit(1);
145: }
146:
147: loadClasspath();
148: //cuf = new ModifyClasses();
149:
150: foundClasses = new Hashtable(3000, 0.8f);
151:
152: for (int i = 0; i < sets.length; i++) {
153:
154: // If a set name ends in '.class' then take it as a class
155: // name of the form com.acme.foo.MyClass.class.
156: try {
157:
158: String s = sets[i];
159:
160: if (s.endsWith(".class")) {
161:
162: findDependencies(s.substring(0, s.length() - 6));
163: } else {
164:
165: FileInputStream fis = new FileInputStream(s);
166:
167: Properties pset = new Properties();
168:
169: pset.load(fis);
170:
171: findClasses(pset);
172: }
173:
174: } catch (IOException ioe) {
175: System.err.println(ioe.toString());
176: System.exit(1);
177: }
178: }
179: if (pwOut == null) {
180: System.out.println("Need to specify an outputfile");
181: System.exit(1);
182: }
183: for (Enumeration e = foundClasses.keys(); e.hasMoreElements();) {
184: String name = (String) e.nextElement();
185: String type = (String) foundClasses.get(name);
186: if (type.equals("class")) {
187: if (ignoreWebLogic) {
188: if (name.startsWith("weblogic")) {
189: continue;
190: }
191: }
192:
193: if (isCloudscapeCode(name)) {
194:
195: if (name.startsWith("com.ibm.db2j.porting.")) {
196: if (cloudscapeOnly)
197: continue;
198: } else {
199: if (portingOnly)
200: continue;
201: }
202:
203: } else {
204: if (cloudscapeOnly || portingOnly)
205: continue;
206: }
207: pwOut.println(name.replace('.', '/') + ".class");
208: } else {
209: // is a file name
210: if (name.startsWith("com/ibm/db2j/")) {
211: if (portingOnly) {
212: continue;
213: }
214: } else {
215: if (cloudscapeOnly || portingOnly)
216: continue;
217: }
218:
219: pwOut.println(name);
220: }
221: }
222: if (showAll) {
223: showAllItems();
224: }
225: if (showOne) {
226: showAllItemsOneLevel();
227: }
228: }
229:
230: protected void findClasses(Properties pset) throws IOException {
231:
232: for (Enumeration e = pset.propertyNames(); e.hasMoreElements();) {
233: String key = (String) e.nextElement();
234: if (key.startsWith("derby.module.")) {
235: if (verbose) {
236: pwOut.println(pset.getProperty(key) + " needs ");
237: }
238: findDependencies(pset.getProperty(key));
239: }
240: }
241: }
242:
243: protected void loadClasspath() {
244: classpathHash = new Hashtable();
245: try {
246: String classpathString = System
247: .getProperty("java.class.path");
248: if (verbose)
249: pwOut.println("classpath: " + classpathString);
250: StringTokenizer st = new StringTokenizer(classpathString,
251: File.pathSeparator);
252: int entries = st.countTokens();
253: classpath = new String[entries];
254: for (int i = 0; i < entries; i++) {
255: classpath[i] = st.nextToken();
256: }
257: } catch (SecurityException se) {
258: pwOut
259: .println("**error** SecurityException getting classpath");
260: System.exit(1);
261: }
262: for (int i = 0; i < classpath.length; i++) {
263: String pathEntry = classpath[i];
264: if (pathEntry.toUpperCase(java.util.Locale.ENGLISH)
265: .endsWith(".ZIP")
266: || pathEntry.toUpperCase(java.util.Locale.ENGLISH)
267: .endsWith(".JAR")) {
268: ZipFile zipfile = null;
269: try {
270: zipfile = new ZipFile(pathEntry.replace('/',
271: File.separatorChar));
272: } catch (IOException ioe) {
273: // can't do anything about it; zipfile doesn't exists
274: // it can happen if the person sticks a directory called
275: // foo.zip in the classpath or foo.zip doesn't exist as
276: // a file
277: }
278: if (zipfile != null) {
279:
280: classpathHash.put(pathEntry, zipfile);
281: } else {
282: if (verbose) {
283: pwOut.println("Ignoring <zip> entry: "
284: + pathEntry);
285: }
286:
287: }
288: } else {
289: File file = new File(pathEntry);
290:
291: if (file.exists() && file.isDirectory()) {
292: classpathHash.put(pathEntry, file);
293: } else {
294: if (verbose) {
295: pwOut.println("Ignoring <dir> entry: "
296: + pathEntry);
297: }
298: }
299: }
300: }
301: }
302:
303: protected InputStream locateClass(String className,
304: boolean beVerbose) {
305: if (className.startsWith("/")) {
306: className = className.substring(1);
307: }
308: if (beVerbose) {
309: pwOut.println("Looking for " + className);
310: }
311:
312: if (classpath == null) {
313: loadClasspath();
314: }
315:
316: for (int i = 0; i < classpath.length; i++) {
317: String pathEntry = classpath[i];
318: Object hash = classpathHash.get(pathEntry);
319: if (hash != null) {
320: if (hash instanceof ZipFile)
321: try {
322: ZipFile zipfile = (ZipFile) hash;
323:
324: ZipEntry entry = zipfile.getEntry(className);
325:
326: if (entry != null) {
327: InputStream is = zipfile
328: .getInputStream(entry);
329: DataInputStream dis = new DataInputStream(
330: new BufferedInputStream(is));
331: return dis;
332: }
333: } catch (IOException ioe) {
334: if (beVerbose) {
335: pwOut
336: .println("IOException loading ZipFile or creating InputStream "
337: + " from it");
338: pwOut.println(ioe);
339: }
340: }
341: else if (hash instanceof File) {
342: File file = new File((File) hash, className
343: .replace('/', File.separatorChar));
344: if (beVerbose) {
345: pwOut.println("looking to load file: "
346: + file.getName());
347: }
348: if (file.exists()) {
349: if (beVerbose) {
350: pwOut.println(" found it!");
351: }
352: try {
353: FileInputStream fis = new FileInputStream(
354: file);
355: return new BufferedInputStream(fis, 8192);
356: } catch (IOException ioe) {
357: if (beVerbose) {
358: pwOut
359: .println("IOException creating FileInputStream");
360: pwOut.println(ioe);
361: return null;
362: }
363: }
364: }
365: }
366: }
367: //
368: }
369:
370: // could not find it
371: if (beVerbose) {
372: pwOut.println("returing null on purpose");
373: }
374: return null;
375: }
376:
377: protected void findDependencies(String className)
378: throws IOException {
379: indent++;
380: try {
381: if (className.startsWith("java.") && skipJava) {
382: pwOut.println("Skipping JAVA " + className);
383: return;
384: }
385: if (className.startsWith("javax.") && skipJavax) {
386: //System.out.println("Skipping JAVAX " + className);
387: return;
388: }
389: if (className.startsWith("sun.") && skipSun) {
390: //System.out.println("Skipping Sun " + className);
391: return;
392: }
393: if (className.startsWith("org.") && skipOrg) {
394: // Allow opensource org.apache.derby classes
395: if (!className.startsWith("org.apache.derby")) {
396: //System.out.println("Skipping org " + className);
397: return;
398: }
399: }
400: if (className.startsWith("com.informix.") && skipInformix) {
401: //System.out.println("Skipping Informix " + className);
402: return;
403: }
404: if (className.startsWith("com.ibm.mobileservices.")
405: && skipDB2e) {
406: //System.out.println("Skipping DB2e " + className);
407: return;
408: }
409: if (className.startsWith("common.") && skipDB2) {
410: //System.out.println("Skipping DB2 common " + className);
411: return;
412: }
413:
414: if (ignoreWebLogic) {
415: if (className.startsWith("weblogic.")) {
416: return;
417: }
418: }
419:
420: if (db2jtools || db2jdrda) {
421:
422: // for tools skip classes that are part of the db2j product api
423: // they should be pulled in from cs.jar or any client.jar
424: if (className
425: .startsWith("org.apache.derby.authentication.")
426: || className
427: .startsWith("org.apache.derby.catalog.")
428: || className
429: .startsWith("org.apache.derby.iapi.db.")
430: || className
431: .startsWith("org.apache.derby.diag.")
432: || className
433: .startsWith("org.apache.derby.jdbc.")
434: || className
435: .startsWith("org.apache.derby.vti.")) {
436: return;
437: }
438: }
439:
440: // drda explicitly brings in some database engine classes.
441: // they must be picke dup from cs.jar and not put in
442: // the network server jar.
443: if (db2jdrda) {
444:
445: if (className.startsWith("org.apache.derby.impl.sql")
446: || className
447: .startsWith("org.apache.derby.impl.jdbc")
448: || className
449: .startsWith("org.apache.derby.impl.services")
450: || className
451: .startsWith("org.apache.derby.iapi.")) {
452: return;
453: }
454: }
455:
456: // already seen class
457: if (foundClasses.get(className) != null)
458: return;
459:
460: if (verbose) {
461: for (int i = 0; i < indent; i++) {
462: System.out.print(".");
463: }
464: System.out.println(className);
465: }
466:
467: /*
468: org.apache.derby.iapi.reference.ClassName &
469: RegisteredFormatIds has a list of all registered classes, If we pull this in then
470: we will pull in the complete set of classes. So we add this to our list but don't
471: dependency check it.
472: */
473: boolean dontCheckDependencies = false;
474: /*
475: if (className.equals("org.apache.derby.iapi.reference.ClassName") ||
476: className.equals("org.apache.derby.iapi.services.io.RegisteredFormatIds")) {
477: dontCheckDependencies = true;
478: }
479: */
480:
481: try {
482: Hashtable localHashtable = null;
483:
484: if (keepDependencyHistory) {
485: localHashtable = (Hashtable) masterClassList
486: .get(className);
487: if (localHashtable == null) {
488: localHashtable = new Hashtable();
489: masterClassList.put(className, localHashtable);
490: }
491: }
492:
493: foundClasses.put(className, "class");
494:
495: if (dontCheckDependencies)
496: return;
497:
498: String fileName = "/" + className.replace('.', '/')
499: + ".class";
500:
501: InputStream is = locateClass(fileName, false);
502:
503: if (is == null) {
504: pwOut
505: .println("**error** Got NULL when looking for fileName = "
506: + fileName);
507: if (!keepRolling) {
508: System.exit(1);
509: } else {
510: errorCount++;
511: }
512: }
513: //byte[] classData = new byte[is.available()];
514: //is.read(classData);
515:
516: ClassInvestigator ch = ClassInvestigator.load(is);
517: is.close();
518:
519: for (Enumeration e = ch
520: /*.getClassInfo()*/.referencedClasses(); e
521: .hasMoreElements();) {
522: String x = (String) e.nextElement();
523: // skip microsoft classes
524: if (x.startsWith("com.ms.")) {
525: continue;
526: }
527:
528: if (!org.apache.derby.iapi.services.sanity.SanityManager.DEBUG) {
529: if (x.indexOf("SanityManager") != -1) {
530:
531: boolean printSanityWarning = true;
532:
533: int ld = className.lastIndexOf(".");
534: if (ld != -1) {
535: if (className.lastIndexOf("T_") == ld + 1)
536: printSanityWarning = false;
537: else if (className.lastIndexOf("T_") == ld + 1)
538: printSanityWarning = false;
539: else if (className.lastIndexOf("D_") == ld + 1)
540: printSanityWarning = false;
541: else if (className.lastIndexOf("TEST_") == ld + 1)
542: printSanityWarning = false;
543: else if (className
544: .endsWith("SanityManager"))
545: printSanityWarning = false;
546: }
547:
548: if (printSanityWarning)
549: System.out.println("SANITY >>> "
550: + fileName);
551: }
552: }
553:
554: if (keepDependencyHistory
555: && (localHashtable.get(x) == null)) {
556:
557: localHashtable.put(x, "class");
558: }
559: findDependencies(x);
560: }
561: } catch (NullPointerException npe) {
562: pwOut
563: .println("**error** Got NullPointerException in findDependencies when looking up ");
564: pwOut.println(className);
565:
566: npe.printStackTrace();
567: if (!keepRolling) {
568: System.exit(1);
569: }
570: errorCount++;
571: }
572:
573: // look for properties only with cloudscape code ...
574: if (!isCloudscapeCode(className))
575: return;
576:
577: // The following block of code checks the package of each class
578: // scanned to see if there is a corresponding properties file
579: // from propFiles and adds it to the list of found classes.
580: // derbytools.jar should not contain any of these files, so skip
581: // for that jar. See also DERBY-1537.
582: if (!db2jtools) {
583: String packageName = className.substring(0, className
584: .lastIndexOf('.') + 1);
585:
586: for (int i = 0; i < propFiles.length; i++) {
587: String fileName = "/"
588: + packageName.replace('.', '/')
589: + propFiles[i];
590: if (foundClasses.get(fileName) != null)
591: continue;
592:
593: InputStream is = getClass().getResourceAsStream(
594: fileName);
595: if (is == null)
596: continue;
597: is.close();
598:
599: foundClasses.put(fileName.substring(1), "file");
600: }
601: }
602: } finally {
603: indent--;
604: }
605: }
606:
607: protected boolean isCloudscapeCode(String name) {
608: return name.startsWith("com.ibm.db2j.")
609: || name.startsWith("com.ihost.cs.")
610: || name.startsWith("db2j.")
611: || name.startsWith("org.apache.derby");
612: }
613:
614: protected void showAllItems() {
615: Enumeration e = masterClassList.keys();
616: pwOut
617: .println("------------Printing all dependents--------------");
618: while (e.hasMoreElements()) {
619: String kid = (String) e.nextElement();
620: pwOut.println(kid);
621: Hashtable scoreboard = new Hashtable();
622: Hashtable grandkids = (Hashtable) masterClassList.get(kid);
623: unrollHashtable("", grandkids, scoreboard, 1);
624: }
625: }
626:
627: protected void showAllItemsOneLevel() {
628: pwOut.println("Showing all dependencies");
629: pwOut.println("One level only");
630: pwOut.println("-----------------------------------");
631:
632: Enumeration e = masterClassList.keys();
633: while (e.hasMoreElements()) {
634: String key = (String) e.nextElement();
635: pwOut.println(key);
636: Hashtable h = (Hashtable) masterClassList.get(key);
637: Enumeration e2 = h.keys();
638: Hashtable h2 = new Hashtable();
639: while (e2.hasMoreElements()) {
640: String key2 = (String) e2.nextElement();
641: pwOut.println("\t" + key2);
642: }
643: }
644: }
645:
646: protected void unrollHashtable(String parent, Hashtable current,
647: Hashtable scoreboard, int indentLevel) {
648: String indentString = " ";
649: Enumeration e = current.keys();
650: String key = null;
651:
652: while (e.hasMoreElements()) {
653: key = (String) e.nextElement();
654: if (key.equals(parent)) {
655: continue;
656: }
657: pwOut.print(indentLevel + ":");
658:
659: Integer value = (Integer) scoreboard.get(key);
660: if (value != null) {
661: for (int i = 0; i < indentLevel; i++) {
662: pwOut.print(indentString);
663: }
664: pwOut.println(key
665: + "*****REPEATED class back at level " + value
666: + "****");
667: return;
668: }
669: for (int i = 0; i < indentLevel; i++) {
670: pwOut.print(indentString);
671: }
672: pwOut.println(key);
673:
674: Hashtable currentsChildren = (Hashtable) masterClassList
675: .get(key);
676: scoreboard.put(key, new Integer(indentLevel));
677: unrollHashtable(key, currentsChildren, scoreboard,
678: (indentLevel + 1));
679: scoreboard.put(key, new Integer(indentLevel));
680:
681: }
682: }
683:
684: }
|