/* * Copyright 2002-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package org.visage.tools.jaranalyzer; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; /** * This tool is used to compute from a jar file the size in kbytes used by each * package found, as well as the total size. * The result files have the right format for being read by the Plot plugin of * Hudson. * Typical use is to call that tool from an Ant target. * Parameters are: * <ul> * <li>the path to the jar file * <li>the path to a place top write result files * <li>a URL value that points to one of the result file, the HTML array that * lists size per package and total size. * </ul> * * There's alse a legacy way of call it manually in order to compare two * successive jar files. * * @author ksrini */ public class JarAnalyzer { static String getPackageName(String name) { String out = name.substring(0, name.lastIndexOf("/")); return out.replace("/", "."); } static Hashtable<String, PkgEntry> readJarFile(URL url) { FileInputStream fis = null; try { HttpURLConnection conn = (HttpURLConnection) url.openConnection(); return readJarFile(conn.getInputStream()); } catch (IOException ioe) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ioe); } finally { try { if (fis != null) { fis.close(); } } catch (IOException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } } return null; } static Hashtable<String, PkgEntry> readJarFile(File inFile) { FileInputStream fis = null; try { fis = new FileInputStream(inFile); return readJarFile(fis); } catch (FileNotFoundException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } finally { try { if (fis != null) { fis.close(); } } catch (IOException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } } return null; } static Hashtable<String, PkgEntry> readJarFile(InputStream in) { Hashtable<String, PkgEntry> tbl = new Hashtable<String, PkgEntry>(); try { ZipInputStream zis = new ZipInputStream(in); ZipEntry ze = zis.getNextEntry(); while (ze != null) { String zname = ze.getName(); if (zname.endsWith(".class")) { String pkgname = getPackageName(zname); if (!tbl.containsKey(pkgname)) { long csize = getCompressedSize(zis); tbl.put(pkgname, new PkgEntry(ze.getSize(), csize)); } else { PkgEntry pe = tbl.get(pkgname); long csize = getCompressedSize(zis); pe.addSizes(ze.getSize(), csize); tbl.put(pkgname, pe); } } ze = zis.getNextEntry(); } } catch (ZipException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } return tbl; } static void dumpToFile(String name1, String name2, OutputStream ostream, Hashtable<String, PkgEntry> tbl1, Hashtable<String, PkgEntry> tbl2) { List<String> keyList = new ArrayList<String>(); for (String x : Collections.list(tbl1.keys())) { keyList.add(x); } Collections.sort(keyList); PrintWriter pw = null; pw = new PrintWriter(new OutputStreamWriter(ostream)); pw.printf("\t%s\t%s\n", name1, name2); long sum1 = 0L; long sum2 = 0L; for (String x : keyList) { pw.printf("%s\t%s\t%s\n", x, tbl1.get(x).getSize() / 1024, tbl2.get(x).getSize() / 1024); sum1 += tbl1.get(x).getSize(); sum2 += tbl2.get(x).getSize(); } pw.printf("Total\t%s\t%s\n", sum1 / 1024, sum2 / 1024); pw.flush(); } static void dumpToFile(String name, OutputStream ostream, Hashtable<String, Long> tbl) { List<String> keyList = new ArrayList<String>(); for (String x : Collections.list(tbl.keys())) { keyList.add(x); } Collections.sort(keyList); PrintWriter pw = null; pw = new PrintWriter(new OutputStreamWriter(ostream)); pw.println(name); long sum = 0L; for (String x : keyList) { pw.printf("%s\t%s\n", x, tbl.get(x) / 1024); sum += tbl.get(x); } pw.printf("Total\t%s\n", sum / 1024); pw.flush(); } static void dumpToFileAllPackages( Hashtable<String, PkgEntry> tbl, String outputRootDir, String urlDir) throws IOException { // report.properties file // This is the input file for the plotter plugin File file1 = new File(outputRootDir + "/staticsizes"); file1.createNewFile(); OutputStream ostream1 = new FileOutputStream(file1); // report.properties.html file // This is the URL to access detailed packages informations File file2 = new File(outputRootDir + "/staticsizes.html"); file2.createNewFile(); OutputStream ostream2 = new FileOutputStream(file2); File file3 = new File(outputRootDir + "/staticsizes.file-count"); file3.createNewFile(); OutputStream ostream3 = new FileOutputStream(file3); List<String> keyList = new ArrayList<String>(); for (String x : Collections.list(tbl.keys())) { keyList.add(x); } Collections.sort(keyList); // Build the html table detailed packages informations PrintWriter pw2 = new PrintWriter(new OutputStreamWriter(ostream2)); pw2.printf("<HTML>\n"); pw2.printf("<BODY LANG=\"en-US\" DIR=\"LTR\">\n"); pw2.printf("<CENTER>\n"); pw2.printf("\t<TABLE WIDTH=420 BORDER=1 CELLPADDING=4 CELLSPACING=0>\n"); pw2.printf("\t<TR VALIGN=TOP>\n"); pw2.printf("\t\t<TH WIDTH=308>"); pw2.printf("<P>Package</P>"); pw2.printf("</TH>\n"); pw2.printf("\t\t<TD WIDTH=95>"); pw2.printf("<P><B>Size (uncompressed) in kbytes</B></P></TD>\n"); pw2.printf("\t\t<TD WIDTH=95>"); pw2.printf("<P><B>Size (compressed) in kbytes</B></P></TD>\n"); pw2.printf("\t\t<TD WIDTH=50>"); pw2.printf("<P><B>File Count</B></P></TD>\n"); pw2.printf("\t</TR>\n"); long sum = 0L; long csum = 0L; int fcount = 0; for (String x : keyList) { long sz = tbl.get(x).getSize(); sum += sz; long csz = tbl.get(x).getCompressedSize(); csum += csz; int n = tbl.get(x).getCount(); fcount += n; pw2.printf("\t<TR VALIGN=TOP>\n"); pw2.printf("\t\t<TD WIDTH=308>"); pw2.printf("<P>" + x + "</P></TD>\n"); pw2.printf("\t\t<TD WIDTH=95>"); pw2.printf("<P>" + sz / 1024 + "</P></TD>\n"); pw2.printf("\t\t<TD WIDTH=50>"); pw2.printf("<P>" + csz / 1024 + "</P></TD>\n"); pw2.printf("\t\t<TD WIDTH=50>"); pw2.printf("<P>" + n + "</P></TD>\n"); pw2.printf("\t</TR>\n"); } pw2.printf("\t<TR VALIGN=TOP>\n"); pw2.printf("\t\t<TD WIDTH=308>"); pw2.printf("<P><B>Total</B></P></TD>\n"); pw2.printf("\t\t<TD WIDTH=95>"); pw2.printf("<P>" + sum / 1024 + "</P></TD>\n"); pw2.printf("\t\t<TD WIDTH=95>"); pw2.printf("<P>" + csum / 1024 + "</P></TD>\n"); pw2.printf("\t\t<TD WIDTH=50>"); pw2.printf("<P>" + fcount + "</P></TD>\n"); pw2.printf("\t</TR>\n"); pw2.printf("\t</TABLE>\n"); pw2.printf("</CENTER>\n"); pw2.printf("</BODY>\n"); pw2.printf("</HTML>\n"); pw2.flush(); pw2.close(); PrintWriter pw1 = new PrintWriter(new OutputStreamWriter(ostream1)); pw1.printf("YVALUE=%s\n", sum / 1024); pw1.printf("URL=%s\n", urlDir + "/staticsizes.html"); pw1.flush(); pw1.close(); PrintWriter pw3 = new PrintWriter(new OutputStreamWriter(ostream3)); pw3.printf("YVALUE=%s\n", fcount); pw3.printf("URL=%s\n", urlDir + "/staticsizes.html"); pw3.flush(); pw3.close(); } private static final String BLDTAG = "BLDTAG"; private static final String VISAGE = "http://visage.org/hudson/job/visage-compiler/" + BLDTAG + "/artifact/dist/lib/shared/visagert.jar"; private static JarStat getTotals(File file) { ZipFile zf = null; JarStat js = null; try { js = new JarStat(file); zf = new ZipFile(file); for (ZipEntry ze : Collections.list(zf.entries())) { js.addSizes(zf, ze); } } catch (ZipException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } finally { if (zf != null) { try { zf.close(); } catch (IOException ex) { } } } return js; } /** * @param args the command line arguments */ public static void main(String[] args) { if (args == null) { System.err.println("Usage: input_jar_file output_root_dir url_dir"); System.err.println("Usage: --compare bld# bld#"); System.exit(1); } else if (args[0].endsWith("compare")) { try { URL url1 = new URL(VISAGE.replace(BLDTAG, args[1])); URL url2 = new URL(VISAGE.replace(BLDTAG, args[2])); Hashtable<String, PkgEntry> tbl1 = readJarFile(url1); Hashtable<String, PkgEntry> tbl2 = readJarFile(url2); dumpToFile(args[1], args[2], System.out, tbl1, tbl2); } catch (MalformedURLException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); } } else { try { String inputJarFile = args[0]; String outputRootDir = args[1]; String urlDir = args[2]; Hashtable<String, PkgEntry> tbl = readJarFile(new File(inputJarFile)); // Plot information for all packages dumpToFileAllPackages(tbl, outputRootDir, urlDir); JarStat js = getTotals(new File(inputJarFile)); File outputFile = new File(outputRootDir + "/staticsizes." + "jar-size-compressed"); js.printSize(outputFile, true, urlDir); outputFile = new File(outputRootDir + "/staticsizes." + "jar-size-uncompressed"); js.printSize(outputFile, false, urlDir); // Plot information for single package for (String key : tbl.keySet()) { outputFile = new File(outputRootDir + "/staticsizes." + key); outputFile.createNewFile(); FileOutputStream ostream = new FileOutputStream(outputFile); PrintWriter pw = new PrintWriter(new OutputStreamWriter(ostream)); pw.printf("YVALUE=%s\n", tbl.get(key).getSize() / 1024); pw.flush(); pw.close(); } } catch (IOException ex) { Logger.getLogger(JarAnalyzer.class.getName()).log(Level.SEVERE, null, ex); System.exit(1); } } System.exit(0); } static long getCompressedSize(InputStream is) { DeflaterOutputStream dos = null; long size = 0L; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Deflater def = new Deflater(); def.setLevel(Deflater.BEST_COMPRESSION); dos = new DeflaterOutputStream(baos, def); byte buf[] = new byte[8192]; int n = is.read(buf); while (n > 0) { dos.write(buf); n = is.read(buf); } dos.flush(); dos.finish(); size = baos.size(); } catch (IOException ex) { Logger.getLogger(JarStat.class.getName()).log(Level.SEVERE, null, ex); } finally { try { dos.close(); } catch (IOException ex) { Logger.getLogger(JarStat.class.getName()).log(Level.SEVERE, null, ex); } } return size; } } class PkgEntry { private long size; private long csize; private int count; private PkgEntry() {} PkgEntry(long sz, long csz) { this.size = sz; this.csize = csz; this.count = 1; } void addSizes(long sz, long csz) { this.size += sz; this.csize += csz; this.count++; } long getSize() { return this.size; } long getCompressedSize() { return this.csize; } int getCount() { return this.count; } } class JarStat { private File jarFile; private long size; // uncompressed sizes private long csize; // compressed sizes JarStat(File jarFile) { this.jarFile = jarFile; size = 0L; csize = 0L; } void addSizes(ZipFile zf, ZipEntry ze) { long sz = ze.getSize(); if (sz == 0) { // don't bother with 0 file size return; } this.size += sz; GZIPOutputStream gzos = null; try { InputStream zis = zf.getInputStream(ze); this.csize += JarAnalyzer.getCompressedSize(zis); } catch (IOException ex) { Logger.getLogger(JarStat.class.getName()).log(Level.SEVERE, null, ex); } } void printSize(File outFile, boolean reportCompressed, String urlDir) { try { outFile.createNewFile(); OutputStream ostream = new FileOutputStream(outFile); PrintWriter pw = new PrintWriter(ostream); pw.println("YVALUE=" + ((reportCompressed) ? this.csize : this.size) / 1024); if (urlDir != null) { pw.println("URL=" + urlDir + "/staticsizes.html"); } pw.close(); } catch (IOException ex) { Logger.getLogger(JarStat.class.getName()).log(Level.SEVERE, null, ex); } } }