Package sos.util

Source Code of sos.util.JarMerge

/********************************************************* begin of preamble
**
** Copyright (C) 2003-2010 Software- und Organisations-Service GmbH.
** All rights reserved.
**
** This file may be used under the terms of either the
**
**   GNU General Public License version 2.0 (GPL)
**
**   as published by the Free Software Foundation
**   http://www.gnu.org/licenses/gpl-2.0.txt and appearing in the file
**   LICENSE.GPL included in the packaging of this file.
**
** or the
** 
**   Agreement for Purchase and Licensing
**
**   as offered by Software- und Organisations-Service GmbH
**   in the respective terms of supply that ship with this file.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
** IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
** THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
********************************************************** end of preamble*/

package sos.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarException;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Pattern;

public class JarMerge {
 
// Eigenschaften der Kommandozeile
 
  // Kommandos
  private boolean check = false;
  private boolean unzip = false;
  private boolean zip = false;
  private boolean add = false;
 
  // Schalter
  private boolean silent = false; // unterdr�ckt bei check die Ausgabe, falls keine Duplikate gefunden werden
  private boolean oldOverwrites = false; // bei unzip: wenn true, alte Dateien �berschreiben neue
  private boolean standardManifestToCreate = false;
  private Pattern fileFilterPattern = Pattern.compile(".*");
  private int compression = -1;
  // l�st die Dateien des jars von der Paketstruktur => Dateien werden direkt ins Zielverzeichnis entpackt
  private boolean detach = false;
 
  // Dateiparameter
  private String source = null;
  private String targetPath = null;
  private String manifestFile = null;
 
  // Der Name dieser Klasse
  private String identity = null;
 
  private JarMerge(String[] args) {
   
    this.identity = this.getClass().getName();
   
    try {
     
      this.parseArguments(args);
     
    } catch (Exception e) {
      this.loglnError("\n abort: "+e.getMessage());
      this.printUsage();
      System.exit(-1);
    }
  }
 
  private void log(String msg) {
    System.out.print(msg);
  }
 
  private void logln(String msg) {
    System.out.println(msg);
  }
 
  private void loglnError(String msg) {
    System.err.println(msg);
  }
 
  private void printUsage() {
   
    String usage = "\n usage:";
    usage += " java "+this.identity+" [-s] check PATH";
    usage += "\n";
    usage += "\n        -all jar/zip archives in directory PATH are tested";
    usage += "\n         for duplicate classnames respecting the package structure";
    usage += "\n         -s: silent mode, if no duplicates are found nothing will be written to console";
    usage += "\n";
    usage += "\n        java "+this.identity+" [-o] [-f=REGEXP] [-d] unzip SOURCE [TARGETPATH]";
    usage += "\n";
    usage += "\n        -extract all files contained in jar/zip archive(s) to subdirectories";
    usage += "\n         relative to package structure.";
    usage += "\n         SOURCE can be a directory or a single archive.";
    usage += "\n         If the directory TARGETPATH is set the structure of subdirectories is created there.";
    usage += "\n         By default the structure of subdirectories is created in current working directory.";
    usage += "\n         If the flag -o is set older files overwrite newer ones.";
    usage += "\n         By default newer files overwrite older ones.";
    usage += "\n         -f: file filter for extracting, REGEXP is a regular expression";
    usage += "\n         -d: (detach) all files are extracted directly to target directory";
    usage += "\n             without attending to package structure";
    usage += "\n";
    usage += "\n        java "+this.identity+" [-m] [-f=REGEXP] [-c=LEVEL] zip SOURCEPATH JARFILE [MANIFESTFILE]";
    usage += "\n";
    usage += "\n        -all files of directory SOURCEPATH and subdirectories are compressed";
    usage += "\n         to jar archive file JARFILE.";
    usage += "\n         If a manifest file is set for MANIFESTFILE this file will be added to jar archive.";
    usage += "\n         If the flag -m is set a standard manifest will be created.";
    usage += "\n         This flag will be ignored if MANIFESTFILE is set.";
    usage += "\n         By default it will be searched in SOURCEPATH for '/META-INF/MANIFEST.MF'.";
    usage += "\n         If this manifest file is found it will be added to jar archive.";
    usage += "\n         If this manifest file is not found a standard manifest will be created.";
    usage += "\n         -f: file filter for compression, REGEXP is a regular expression";
    usage += "\n         -c: compression level, LEVEL can be 0 to 9";
    usage += "\n";
    usage += "\n        java "+this.identity+" [-m] [-f=REGEXP] [-c=LEVEL] add SOURCEPATH JARFILE [MANIFESTFILE]";
    usage += "\n";
    usage += "\n        -all files of directory SOURCEPATH and subdirectories are added";
    usage += "\n         to existing jar archive file JARFILE.";
    usage += "\n         If JARFILE does not yet exist a new file will be created.";
    usage += "\n         If a manifest file is set for MANIFESTFILE this file will be added to jar archive.";
    usage += "\n         If the flag -m is set a standard manifest will be created.";
    usage += "\n         This flag will be ignored if MANIFESTFILE is set.";
    usage += "\n         By default it will be searched in SOURCEPATH for '/META-INF/MANIFEST.MF'.";
    usage += "\n         If this manifest file is found it will be added to jar archive.";
    usage += "\n         If this manifest file is not found a standard manifest will be created.";
    usage += "\n         -f: file filter for compression, REGEXP is a regular expression";
    usage += "\n         -c: compression level, LEVEL can be 0 to 9. It affects all files in the archive.";
    usage += "\n";
    usage += "\n        java "+this.identity+" -h";
    usage += "\n";
    usage += "\n        -show this help";
   
    this.logln(usage);
  }
 
  private void parseArguments(String[] args) throws Exception {
   
    if ( args==null ) throw new Exception("args is null");
    if ( args.length==0 ) throw new Exception("arguments missing");
   
    // Hilfe
    if ( args[0].toLowerCase().equals("-h") ) {
     
      if ( args.length>1 ) {
        String arg = args[1];
        if ( arg.equals("check") || arg.equals("unzip") || arg.equals("zip") || arg.equals("add") )
          throw new Exception("unexpected command '"+arg+"'");
       
        if ( args[1].length()<2 )
          throw new Exception("unknown parameter '"+args[1]+"'");
       
        arg = args[1].substring(0,2).toLowerCase();
        if ( arg.equals("-s") || arg.equals("-o") || arg.equals("-f") || arg.equals("-m") || arg.equals("-c") || arg.equals("-d") || arg.equals("-h") )
          throw new Exception("unexpected parameter '"+args[1]+"'");
        else
          throw new Exception("unknown parameter '"+args[1]+"'");
      }
     
      this.printUsage();
      System.exit(0);
    }
   
    ArrayList arguments = new ArrayList();
    ArrayList switches = new ArrayList();
   
    for (int i=0; i<args.length; i++) {
     
      if ( args[i].charAt(0)=='-' ) {
        if ( args[i].length()==1 ) {
          if ( args.length==1 )
            throw new Exception("unknown command '-'");
          else
            throw new Exception("unknown parameter '-'");
        } else {
          switches.add(args[i]);
        }
      } else
        arguments.add(args[i]);
    }
   
    if ( arguments.size()==0 ) throw new Exception("unknown command '"+args[0]+"'");
    String command = (String)arguments.get(0);
    int minNrOfArgs = 0;
    int maxNrOfArgs = 0;
   
    if ( command.equals("check") ) {
      this.check = true;
      minNrOfArgs = 1;
      maxNrOfArgs = 1;
    }
    else
    if ( command.equals("unzip") ) {
      this.unzip = true;
      minNrOfArgs = 1;
      maxNrOfArgs = 2;
    }
    else
    if ( command.equals("zip") ) {
      this.zip = true;
      minNrOfArgs = 2;
      maxNrOfArgs = 3;
    }
    else
    if ( command.equals("add") ) {
      this.add = true;
      minNrOfArgs = 2;
      maxNrOfArgs = 3;
    }
    else
      throw new Exception("unknown command '"+command+"'");
   
    // das Kommando aus der Argumentliste enfernen
    arguments.remove(0);
   
    // Anzahl der restlichen Argumente �berpr�fen
    if ( arguments.size()<minNrOfArgs ) throw new Exception("arguments missing");
    if ( arguments.size()>maxNrOfArgs ) throw new Exception("too many arguments");
   
    String sw = null;
   
  // check
    if ( this.check ) {
      this.source = (String)arguments.get(0);
      // switches
      for (int i=0; i<switches.size(); i++) {
        sw = ((String)switches.get(i)).toLowerCase();
       
        if ( sw.equals("-s") )
          this.silent = true;
        else if ( sw.equals("-o") || sw.equals("-m") || sw.equals("-h") || sw.equals("-d") || sw.matches("-f=.+") || sw.matches("-c=.+") )
          throw new Exception("unexpected parameter '"+(String)switches.get(i)+"'");
        else
          throw new Exception("unknown parameter '"+(String)switches.get(i)+"'");
      }
    }
    else
   
  // unzip
    if ( this.unzip ) {
      this.source = (String)arguments.get(0);
      if ( arguments.size()==2 ) this.targetPath = (String)arguments.get(1);
      // switches
      for (int i=0; i<switches.size(); i++) {
        sw = ((String)switches.get(i)).toLowerCase();
       
        if ( sw.equals("-d") )
          this.detach = true;
        else if ( sw.equals("-o") )
          this.oldOverwrites = true;
        else if ( sw.matches("-f=.+") )
          try {
            this.fileFilterPattern = Pattern.compile( ((String)switches.get(i)).substring(3) );
          } catch (Exception e) {
            throw new Exception("cannot compile pattern '"+((String)switches.get(i)).substring(3)+"'");
          }
        else if ( sw.equals("-s") || sw.equals("-m") || sw.equals("-h") || sw.matches("-c=.+"))
          throw new Exception("unexpected parameter '"+(String)switches.get(i)+"'");
        else
          throw new Exception("unknown parameter '"+(String)switches.get(i)+"'");
      }
    }
    else
   
  // zip & add
    if ( this.zip || this.add ) {
      this.source = (String)arguments.get(0);
      this.targetPath = (String)arguments.get(1);
      if ( arguments.size()==3 ) this.manifestFile = (String)arguments.get(2);
      // switches
      for (int i=0; i<switches.size(); i++) {
        sw = ((String)switches.get(i)).toLowerCase();
       
        if ( sw.equals("-m") )
          this.standardManifestToCreate = true;
        else if ( sw.matches("-f=.+") )
          try {
            this.fileFilterPattern = Pattern.compile( ((String)switches.get(i)).substring(3) );
          } catch (Exception e) {
            throw new Exception("cannot compile pattern '"+((String)switches.get(i)).substring(3)+"'");
          }
        else if ( sw.matches("-c=.+") ) {
          try {
            this.compression = Integer.parseInt( ((String)switches.get(i)).substring(3) );
          } catch (Exception e) {
            throw new Exception("cannot convert '"+((String)switches.get(i)).substring(3)+"' to int");
          }
          if ( this.compression<0 || this.compression>9 ) throw new Exception("level of compression must be between 0 and 9");
        } else if ( sw.equals("-s") || sw.equals("-o") || sw.equals("-d") || sw.equals("-h") )
          throw new Exception("unexpected parameter '"+(String)switches.get(i)+"'");
        else
          throw new Exception("unknown parameter '"+(String)switches.get(i)+"'");
      }
    }
   
  }
 
  /**
   * Filtert aus einer Liste von Dateinamen alle Dateinamen mit der Endung jar oder zip heraus.
   * @return gefilterte Liste
   */
  private String[] archiveFilter(String[] list) {
   
    String[] fList;
    String suffix;
    int deleted = 0;
   
    if ( list==null ) return null;
   
    for (int i=0; i<list.length; i++) {
     
      if ( list[i].length()<4 || list[i]==null ) {
        list[i] = null;
        deleted++;
      } else {
        suffix = list[i].substring(list[i].length()-4);
        if ( !(suffix.equals(".jar") || suffix.equals(".zip")) ) {
          list[i] = null;
          deleted++;
        }
      }
    }
   
    fList = new String[list.length-deleted];
   
    for (int n=0, i=0; i<list.length; i++) {
     
      if ( list[i]!=null ) fList[n++] = list[i];
    }
   
    return fList;
  }
 
  /**
   * Filtert aus einer Liste von Dateien alle Dateien, die dem regul�ren Ausdruck pattern gen�gen heraus.
   * Unabh�ngig davon werden das zu erstellende jar und META-INF/MANIFEST.MF entfernt.
   * @return gefilterte Liste von Dateien
   */
  private File[] fileFilter(File[] list, Pattern pattern) throws Exception {
   
    String newjarPath = (new File(this.targetPath)).getCanonicalPath();
   
    File[] fList;
    int deleted = 0;
   
    if ( list==null ) return null;
   
    for (int i=0; i<list.length; i++) {
      // die zu erstellende jardatei soll auch weggefiltert werden
      // das Manifest wird extra hinzugef�gt => hier wegfiltern
      if ( list[i]==null
          || list[i].getCanonicalPath().equals(newjarPath)
          || Pattern.matches(".*META-INF[/\\\\]MANIFEST[.]MF", list[i].getPath())
          || !(pattern.matcher(list[i].getName()).matches())
          ) {
       
        list[i] = null;
        deleted++;
      }
    }
   
    fList = new File[list.length-deleted];
   
    for (int n=0, i=0; i<list.length; i++)
      if ( list[i]!=null ) fList[n++] = list[i];
   
    return fList;
  }
 
  private void executeCheck() throws Exception {
   
    JarFile jar;
    Enumeration content;
    JarEntry entry;
    String classFile;
    long lDate;
    long size;
   
    StringTokenizer st;
    SimpleDateFormat sdf;
    String firstFile;
    String firstDate;
    String firstSize;
    int gap;
    int duplicates = 0;
   
    File file = new File(this.source);
   
    // alle zu durchsuchenden jar- und zip-Archive ermitteln
    String[] list = this.archiveFilter(file.list());
   
    if ( list.length == 0 ) {
      this.logln("\n no jar/zip archives found");
      return;
    }
   
    Hashtable inventory = new Hashtable();
   
    try {
     
      int i = 0;
      // alle Archive durchgehen
      for (i=0; i<list.length; i++) {
       
        jar = new JarFile(this.source + list[i]);
       
        content = jar.entries();
       
        // alle Klassen eines Archivs durchgehen
        while ( content.hasMoreElements() ) {
         
          entry = (JarEntry)content.nextElement();
          classFile = entry.getName();
          lDate = entry.getTime();
          size = entry.getSize();
         
          if ( classFile.length()>6 )
            if ( classFile.substring(classFile.length()-6).equals(".class") )
              if ( !inventory.containsKey(classFile) ) {
               
                //                 classFile => "x.jar|date|size"
                inventory.put(classFile, list[i]+"|"+String.valueOf(lDate)+"|"+String.valueOf(size));
               
              } else {
               
                st = new StringTokenizer( (String)inventory.get(classFile), "|" );
                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
               
                firstFile = st.nextToken();
                firstDate = st.nextToken();
                firstSize = st.nextToken();
               
                if ( Long.parseLong(firstDate) < 0 )
                  firstDate = "unknown";
                else
                  firstDate = sdf.format(new Date(Long.parseLong(firstDate)));
               
                if ( Long.parseLong(firstSize) < 0 )
                  firstSize = "unknown";
               
                // die gr��te Stringl�nge ermitteln
                gap = Math.max(firstFile.length(), firstDate.length());
                gap = Math.max(gap, firstSize.length());
               
                // und Stringl�nge normalisieren
                while ( firstFile.length()<gap ) firstFile += " ";
                while ( firstDate.length()<gap ) firstDate += " ";
                while ( firstSize.length()<gap ) firstSize += " ";
               
                this.logln("\n duplicate class found: "+classFile);
                this.logln(" "+ firstFile +"   "+ list[i] )// Dateiname
                this.logln(" "+ firstDate +"   "+ ( lDate < 0 ? "unknown" : sdf.format(new Date(lDate)) ) )// Datum               
                this.logln(" "+ firstSize +"   "+ ( size < 0 ? "unknown" : String.valueOf(size) ) )// Dateigr��e
               
                duplicates++;
              }
        }
      }
     
      if (duplicates==0) {
        if ( !this.silent ) {
          this.logln("\n no duplicates found");
          this.logln(" "+i+" archive"+ ((i!=1) ? "s" : "") +" passed");
        }
      } else {
        this.logln("\n "+duplicates+" duplicate"+ ((duplicates!=1) ? "s" : "") +" found");
        this.logln(" "+i+" archive"+ ((i!=1) ? "s" : "") +" passed");
      }
     
    } catch (Exception e) {
      this.loglnError("check error: "+e.getMessage());
    }
  }
 
  /**
   * @return Absoluter Pfad der erstellten Datei
   */
  private String unpackJarEntry( JarFile jarFile, JarEntry jarEntry, String targetPath) throws JarException, IOException, Exception {
   
    try {
     
      if ( jarFile==null ) throw new Exception("jarFile is null");
      if ( jarEntry==null ) throw new Exception("jarEntry is null");
      if ( targetPath==null ) targetPath = "";
     
      String filename = jarEntry.getName();
     
      // Paketstruktur aufl�sen und Datei ohne Unterverzeichnisse direkt nach targetPath entpacken
      if ( this.detach ) {
        int index = filename.lastIndexOf('/') + 1; // behandelt R�ckgabewert -1 implizit mit
        filename = filename.substring(index);
      }
     
      File file = new File( targetPath + filename );
     
      BufferedInputStream bis = new BufferedInputStream( jarFile.getInputStream( jarEntry ) );
      File dir = new File( file.getParent() );
      dir.mkdirs();
     
      BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream( file ) );
     
      for ( int c; ( c = bis.read() ) != -1; )
        bos.write( (byte)c );
     
      bos.close();
      bis.close();
     
      file = new File( targetPath + filename );
      file.setLastModified( jarEntry.getTime() );
     
      return targetPath + filename;
     
    } catch (JarException jax) {
      throw new JarException(jax.getMessage()+": "+this.identity+".unpackJarEntry: "+jarFile.getName() + " => " + targetPath + jarEntry.getName());
    } catch (IOException iox) {
      throw new IOException(iox.getMessage()+": "+this.identity+".unpackJarEntry: "+jarFile.getName() + " => " + targetPath + jarEntry.getName());
    } catch (Exception e) {
      throw new Exception(e.getMessage()+": "+this.identity+".unpackJarEntry: "+jarFile.getName() + " => " + targetPath + jarEntry.getName());
    }
  }
 
  private void createManifestFile(ArrayList iSecName, ArrayList iSecOrigin, ArrayList iSecDate, String targetPath) throws IOException, Exception {
   
    Manifest manifest = null;
    File file = null;
   
    try {
     
      if (iSecName==null || iSecOrigin==null || iSecDate==null)
        throw new Exception("iSec is null");
      if ( !((iSecName.size()==iSecOrigin.size()) && (iSecName.size()==iSecDate.size())) )
        throw new Exception("iSec ArrayList sizes are inconsistent "+iSecName.size()+","+iSecOrigin.size()+","+iSecDate.size());
     
    // Falls keine Klasse entpackt wurde, soll auch kein Manifest geschrieben werden
      if ( iSecName.size()==0 ) return;
     
      if ( targetPath==null ) targetPath = "";
     
      file = new File(targetPath + "META-INF");
      if ( !file.isDirectory() )
        file.mkdir();
     
      file = new File(targetPath + "META-INF/MANIFEST.MF");
      file.createNewFile();
     
      StringBuffer sbuf = new StringBuffer();
      sbuf.append("Manifest-Version: 1.0\n");
      sbuf.append("Created-By: "+System.getProperty("java.vm.version")+" ("+System.getProperty("java.vm.vendor")+")\n");
      sbuf.append("\n");
     
      for (int i=0; i<iSecName.size(); i++) {
        sbuf.append("Name: "+ (String)iSecName.get(i) +"\n");
        sbuf.append("Origin: "+ (String)iSecOrigin.get(i) +"\n");
        sbuf.append("Archive-Date: "+ (String)iSecDate.get(i) +"\n");
        sbuf.append("\n");
      }
     
      // Convert the string to a input stream
      InputStream is = new ByteArrayInputStream(sbuf.toString().getBytes("UTF-8"));
     
      // Create the manifest
      manifest = new Manifest(is);
      manifest.write(new FileOutputStream(file));
     
      this.logln("\n manifest file "+ file.getCanonicalPath() +" created");
     
    } catch (IOException iox) {
      throw new IOException(this.identity+"constructManifest: "+iox.getMessage());
    } catch (Exception e) {
      throw new Exception(this.identity+"constructManifest: "+e.getMessage());
    }
  }
 
  private Manifest readManifestFile(String path) throws IOException {
   
    Manifest manifest = null;
   
    if ( path==null ) return null;
   
    File file = new File(path);
   
    if ( file.canRead() ) {
     
      try {
        manifest = new Manifest(new FileInputStream(file));
      } catch (FileNotFoundException nfx) {
      } catch (IOException iox) {
        throw new IOException(this.identity+"readManifestFile: "+file.getCanonicalPath()+": "+iox.getMessage());
      }
    }
   
    return manifest;
  }
 
  private void executeUnzip() throws Exception {
   
    File file = null;
    JarFile jar;
    Enumeration content;
    JarEntry entry;
    String classFile = "";
    String filename = "";
    long archiveDate;
    long hdDate;
    int extracted = 0;
    int processed = 0;
   
    // individuelle Manifest Sektionen
    ArrayList iSecName = new ArrayList();
    ArrayList iSecOrigin = new ArrayList();
    ArrayList iSecDate = new ArrayList();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    String jarDate;
    int index;
   
    // Verzeichnis der Archive einlesen
    file = new File( this.source );
   
    // alle zu durchsuchenden jar- und zip-Archive ermitteln
    String path = null;
    String[] list = null;
   
    if ( file.isDirectory() ) {
      path = this.source;
      list = this.archiveFilter(file.list());
    } else {
      path = file.getPath().substring(0,file.getPath().length()-file.getName().length());
      list = new String[1];
      list[0] = file.getName();
    }
   
    if ( list.length == 0 ) {
      this.logln("\n no jar/zip archives found");
      return;
    }
   
    if ( this.oldOverwrites )
      this.logln("\n directive: overwrite newer versions");
    else
      this.logln("\n directive: overwrite older versions");
   
    try {
     
      // alle Archive durchgehen
      for (int i=0; i<list.length; i++) {
       
        this.logln("\n unpack "+ list[i] +":");
       
        jar = new JarFile(path + list[i]);
        content = jar.entries();
       
        file = new File(path + list[i]);
        jarDate = ( file.lastModified() < 0 ) ? "unknown" : sdf.format(new Date(file.lastModified()))
       
        // alle Klassen eines Archivs durchgehen
        while ( content.hasMoreElements() ) {
         
          entry = (JarEntry)content.nextElement();
          classFile = entry.getName();
         
          if ( !(classFile.charAt(classFile.length()-1)=='/' || classFile.toUpperCase().equals("META-INF/MANIFEST.MF")) ) {
           
            index = classFile.lastIndexOf('/')+1; // behandelt R�ckgabewert -1 implizit mit
            // Dateiname muss vom regul�ren Ausdruck erfasst werden
            if ( this.fileFilterPattern.matcher(classFile.substring(index)).matches() ) {
             
              // Paketstruktur aufl�sen?
              if ( this.detach )
                filename = classFile.substring(index);
              else
                filename = classFile;
             
              file = new File( ((this.targetPath!=null) ? this.targetPath : "") + filename );
             
              // Datei existiert schon auf Platte
              if ( file.canRead() ) {
               
                hdDate = file.lastModified();
                archiveDate = entry.getTime();
               
                if ( (!this.oldOverwrites && (archiveDate > hdDate)) || (this.oldOverwrites && (archiveDate < hdDate)) ) {
                  this.unpackJarEntry(jar, entry, this.targetPath);
                 
                  // f�r Manifest individuelle Sektionen
                  if ( iSecName.contains(filename) ) {
                    index = iSecName.indexOf(filename);
                   
                    iSecName.remove(index);
                    iSecOrigin.remove(index);
                    iSecDate.remove(index);
                   
                    iSecName.add(index, filename);
                    iSecOrigin.add(index, list[i]);
                    iSecDate.add(index, jarDate);
                   
                  } else {
                    iSecName.add(filename);
                    iSecOrigin.add(list[i]);
                    iSecDate.add(jarDate);
                  }
                 
                  this.logln("  extract and overwrite "+filename);
                  extracted++;
                } else {
                  this.logln("  do not overwrite      "+filename);
                  processed++;
                }
               
              // Datei existiert noch nicht auf Platte
              } else {
               
                this.unpackJarEntry(jar, entry, this.targetPath);
               
                this.logln("  extract and write     "+filename);
                iSecName.add(filename);
                iSecOrigin.add(list[i]);
                iSecDate.add(jarDate);
                extracted++;
              }
            }
          }
        }
      }
     
      this.createManifestFile(iSecName, iSecOrigin, iSecDate, this.targetPath);
     
      processed += extracted;
     
      this.logln("\n "+ list.length +" archive"+ ((list.length != 1) ? "s" : "") +" passed");
      this.logln(" "+ processed +" file"+ ((processed != 1) ? "s" : "") +" passed");
      this.logln(" "+ extracted +" file"+ ((extracted != 1) ? "s" : "") +" extracted");
     
    } catch (Exception e) {
      this.loglnError("unzip error: "+ e.getMessage() +": "+ classFile);
    }
  } 
 
  private void createJarArchive(File jarFile, File[] tobeJared, String manifestFile) throws IOException, Exception {
   
    if ( jarFile==null ) throw new Exception("jarFile is null");
    if ( tobeJared==null ) throw new Exception("array of files is null");
    if ( tobeJared.length==0 ) throw new Exception("no files to archive");
   
    int BUFFER_SIZE = 10240;
    String path;
   
    try {
     
      byte buffer[] = new byte[BUFFER_SIZE];
     
      // Archiv �ffnen
      FileOutputStream stream = new FileOutputStream(jarFile);
     
      Manifest manifest = readManifestFile(manifestFile);
     
      if ( manifest!=null ) {
        File file = new File(manifestFile);
        this.logln("\n adding existing manifest file "+file.getCanonicalPath());
     
      } else {
        this.logln("\n create new manifest");
        StringBuffer sbuf = new StringBuffer();
        sbuf.append("Manifest-Version: 1.0\n");
        sbuf.append("Created-By: "+System.getProperty("java.vm.version")+" ("+System.getProperty("java.vm.vendor")+")\n");
        sbuf.append("\n");
       
        // Den String in InputStream �berf�hren
        InputStream is = new ByteArrayInputStream(sbuf.toString().getBytes("UTF-8"));
       
        // Manifest erzeugen
        manifest = new Manifest(is);
      }
     
      JarOutputStream out = new JarOutputStream(stream, manifest);
     
      if ( this.compression != -1)
        out.setLevel(this.compression);
     
      JarEntry jarAdd;
     
      for (int i = 0; i < tobeJared.length; i++) {
       
        if ( tobeJared[i] == null )
          continue;
        if ( !tobeJared[i].exists() || tobeJared[i].isDirectory() )
          continue;
       
        // Zielpfad der Datei auf die package-Struktur reduzieren
        path = tobeJared[i].getPath().substring(this.source.length());
        path = path.replaceAll("\\\\", "/");
       
        this.log("\n adding " + path);
       
        // Add archive entry
        jarAdd = new JarEntry(path);
        jarAdd.setTime(tobeJared[i].lastModified());
        out.putNextEntry(jarAdd);
       
        // Klasse ins Archiv schreiben
        FileInputStream in = new FileInputStream(tobeJared[i]);
       
        while (true) {
         
          int nRead = in.read(buffer, 0, buffer.length);
          if ( nRead <= 0 )
            break;
         
          out.write(buffer, 0, nRead);
        }
       
        in.close();
      }
     
      out.close();
      stream.close();
     
      this.logln("\n "+tobeJared.length+" file"+ ((tobeJared.length != 1) ? "s" : "") +" added");
     
    } catch (IOException iox) {
      throw new IOException(iox.getMessage()+": "+this.identity+".createJarArchive: "+jarFile.getAbsolutePath());
   
    } catch (Exception e) {
      throw new Exception(e.getMessage()+": "+this.identity+".createJarArchive: "+jarFile.getAbsolutePath());
    }
  }
 
  private void addFilesToExistingJar(File jarFile, File[] tobeJared, String manifestFile) throws IOException, Exception {
   
    if ( jarFile==null ) throw new Exception("jarFile is null");
    if ( tobeJared==null ) throw new Exception("array of files is null");
    if ( tobeJared.length==0 ) throw new Exception("no files to archive");
   
  // Inhalt des bestehenden jars auslesen
    JarFile jar = new JarFile(jarFile);
    Enumeration jarEnum = jar.entries();
    Hashtable jarContent = new Hashtable();
   
    String elem;
    // alle Dateien eines Archivs durchgehen
    while ( jarEnum.hasMoreElements() ) {
      elem = ((JarEntry)jarEnum.nextElement()).getName();
      jarContent.put(elem, "notnull");
    }
   
    // JarFile schliessen
    jar.close();
   
  // Manifest ermitteln
    Manifest manifest = readManifestFile(manifestFile);
    if ( manifest!=null ) {
      File file = new File(manifestFile);
      this.logln("\n adding existing manifest file "+file.getCanonicalPath());
   
    } else {
      this.logln("\n create new manifest");
      StringBuffer sbuf = new StringBuffer();
      sbuf.append("Manifest-Version: 1.0\n");
      sbuf.append("Created-By: "+System.getProperty("java.vm.version")+" ("+System.getProperty("java.vm.vendor")+")\n");
      sbuf.append("\n");
     
      // Den String in InputStream �berf�hren
      InputStream is = new ByteArrayInputStream(sbuf.toString().getBytes("UTF-8"));
     
      // Manifest erzeugen
      manifest = new Manifest(is);
    }
   
    // Zufallszahl als Teil des Namens f�r die tempor�re Datei
    String rand1 = String.valueOf(Math.random()).substring(2);
    String rand2 = String.valueOf(Math.random()).substring(2);
    String randomName = jarFile.getName()+".tmp."+rand1+rand2;
   
    // Namen f�r tempor�re Datei setzen
    String tempfile = jarFile.getCanonicalPath();
    tempfile = tempfile.substring(0,tempfile.length()-jarFile.getName().length())+randomName;
   
    JarInputStream jarIn = new JarInputStream(new FileInputStream(jarFile.getPath()));
    JarOutputStream jarOut = new JarOutputStream(new FileOutputStream( tempfile ), manifest);
   
    if ( this.compression != -1)
      jarOut.setLevel(this.compression);
   
    // We must write every entry from the input JAR to the output JAR, so iterate over the entries:
    // Create a read buffer to transfer data from the input
   
    byte[] buffer = new byte[4096];
    JarEntry entry;
    String path;
    Hashtable inventory = new Hashtable();
   
    int nrOfWrittenFiles = 0;
   
    // add the new files
    for (int i = 0; i < tobeJared.length; i++) {
     
      if ( tobeJared[i] == null )
        continue;
      if ( !tobeJared[i].exists() || tobeJared[i].isDirectory() )
        continue;
     
      // Zielpfad der Datei auf die package-Struktur reduzieren
      path = tobeJared[i].getPath().substring(this.source.length());
      path = path.replaceAll("\\\\", "/");
     
      // Datei ist im Original schon enthalten?
      if ( jarContent.get(path)!=null )
        this.log("\n overwriting " + path);
      else
        this.log("\n      adding " + path);
     
      // Add archive entry
      entry = new JarEntry(path);
      entry.setTime(tobeJared[i].lastModified());
      jarOut.putNextEntry(entry);
     
      // Klasse ins Archiv schreiben
      FileInputStream in = new FileInputStream(tobeJared[i]);
     
      while ( true ) {
       
        int nRead = in.read(buffer, 0, buffer.length);
        if ( nRead <= 0 )
          break;
       
        jarOut.write(buffer, 0, nRead);
      }
     
      inventory.put(entry.getName(), "notnull");
     
      in.close();
      nrOfWrittenFiles++;
    }
   
    // Iterate the entries of the original file
    while ((entry = jarIn.getNextJarEntry()) != null) {
     
      // Exclude the manifest file from the old JAR
      if ("META-INF/MANIFEST.MF".equals(entry.getName())) continue;
     
      // Datei befindet sich bereits im jar
      if ( inventory.get(entry.getName())!=null )
        continue;
     
      // Write the entry to the output JAR
      jarOut.putNextEntry(entry);
      int read;
      while ((read = jarIn.read(buffer)) != -1) {
        jarOut.write(buffer, 0, read);
      }
     
      jarOut.closeEntry();
    }
   
    // Flush and close all the streams
    jarOut.flush();
    jarOut.close();
    jarIn.close();
   
  // altes jar l�schen und neue Datei umbenennen
    File oldJar = new File(this.targetPath);
    try {
      if ( !jarFile.delete() )
        throw new Exception("cannot delete old "+oldJar.getCanonicalPath());
     
      File newJar = new File(tempfile);
     
      if ( !newJar.renameTo(oldJar) )
        throw new Exception("cannot rename tempfile "+tempfile+" to "+oldJar.getCanonicalPath());
     
    } catch (Exception e) {
      throw new Exception("cannot update jar: "+e.getMessage());
    }
   
    this.logln("\n "+nrOfWrittenFiles+" file"+ ((tobeJared.length != 1) ? "s" : "") +" added");
  }
 
  private ArrayList getAllFiles(File file, ArrayList allFiles) {
   
    if ( allFiles==null ) allFiles = new ArrayList();
   
    if ( file.isDirectory() ) {
     
      String[] list = file.list();
     
      for (int i=0; i<list.length; i++)
        this.getAllFiles(new File(file.getPath()+"/"+list[i]), allFiles);
     
    } else {
      allFiles.add(file);
    }
   
    return allFiles;
  }
 
  private void executeZip() throws Exception {
   
    File file = null;
    ArrayList classFiles;
   
    try {
     
    // jar-Archiv �berpr�fen
      file = new File(this.targetPath);
     
      if ( file.isFile() )
        file.delete();
     
      file.createNewFile();
     
    } catch (IOException iox) {
     
      if ( file != null )
        throw new Exception(iox.getMessage()+": "+file.getCanonicalPath());
      else
        throw new Exception(iox.getMessage()+": "+this.targetPath);
    }
   
    File directory = new File(this.source);
    classFiles = new ArrayList();
    classFiles = this.getAllFiles(directory, classFiles);
   
    File[] classes = this.fileFilter((File[])classFiles.toArray(new File[classFiles.size()]), this.fileFilterPattern);
   
    if ( classes!=null && classes.length>0 ) {
     
    // MANIFESTFILE ist gesetzt
      if ( this.manifestFile!=null )
        this.createJarArchive(file, classes, this.manifestFile);
     
    // -m ist gesetzt und MANIFESTFILE ist nicht gesetzt
      else if ( this.standardManifestToCreate )
        this.createJarArchive(file, classes, null);
     
    // default: weder -m noch MANIFESTFILE ist gesetzt
      else
        this.createJarArchive(file, classes, this.source+"META-INF/MANIFEST.MF");
     
      this.logln("\n "+file.getCanonicalPath()+" created");
     
    } else {
      this.logln("\n no files to archive");
    }
  }
 
  // Falls das Zieljar nicht existiert, wie zip
  // ansonsten ausgew�hlte Dateien zu jar hinzuf�gen
  private void executeAdd() throws Exception {
   
    File file = null;
    ArrayList content;
   
    // jar-Archiv �berpr�fen
    file = new File(this.targetPath);
   
    // wenn Datei noch nicht exisitert => executeZip() ausf�hren
    if ( !file.exists() ) {
      this.executeZip();
      return;
     
    } else if ( !file.canWrite() ) {
      try {
        throw new Exception("cannot write "+file.getCanonicalPath());
      } catch (Exception e) {
        throw new Exception("cannot write "+this.targetPath);
      }
    }
   
  // ausgew�hlte Dateien zu bestehendem jar hinzuf�gen
   
    File directory = new File(this.source);
    content = new ArrayList();
    content = this.getAllFiles(directory, content);
   
    File[] filesToAdd = this.fileFilter((File[])content.toArray(new File[content.size()]), this.fileFilterPattern);
   
    if ( filesToAdd!=null && filesToAdd.length>0 ) {
     
      // MANIFESTFILE ist gesetzt
      if ( this.manifestFile!=null )
        this.addFilesToExistingJar(file, filesToAdd, this.manifestFile);
     
      // -m ist gesetzt und MANIFESTFILE ist nicht gesetzt
      else if ( this.standardManifestToCreate )
        this.addFilesToExistingJar(file, filesToAdd, null);
     
      // default: weder -m noch MANIFESTFILE ist gesetzt
      else
        this.addFilesToExistingJar(file, filesToAdd, this.source+"META-INF/MANIFEST.MF");
     
      this.logln("\n "+file.getCanonicalPath()+" updated");
     
    } else {
      this.logln("\n no files to archive");
    }
  }
 
  private void execute() {
   
  // Pfad- und Dateiangaben auf Korrektheit �berpr�fen
    try {
     
      File file = new File(this.source);
     
      if ( !file.canRead() )
        throw new Exception(this.source +" not found or not readable");
     
      if ( this.check || this.zip || this.add) {
        if ( !file.isDirectory() )
          throw new Exception(file.getCanonicalPath() +" is no directory");
         
      } else if ( this.unzip ) {
        if ( !file.isDirectory() ) {
          if ( !file.isFile() ) {
            throw new Exception(this.source +" is no normal file");
          } else {
            if ( this.source.length()<5 )
              throw new Exception(file.getCanonicalPath() +" is no valid archiv");
            else {
              String suffix = this.source.substring(this.source.length()-4);
              if ( !(suffix.equals(".jar") || suffix.equals(".zip")) )
                throw new Exception(file.getCanonicalPath() +" is no valid archiv");
            }
          }
        }
      }
     
    // Quellpfad normalisieren
      if ( file.isDirectory() ) {
        this.source = this.source.replaceAll("\\\\", "/");
        if ( !(this.source.charAt(this.source.length()-1)=='/') )
          this.source += "/";
      }
     
    // zus�tzlich f�r unzip optionales Zielverzeichnis testen
      if ( this.unzip ) {
        if ( this.targetPath!=null ) {
          try {
           
            file = new File(this.targetPath);
           
            if ( !file.isDirectory() ) {
              throw new Exception(this.targetPath +" is no directory");
            } else {
              if ( !file.canWrite() )
                throw new Exception(this.targetPath +" is not writable");
            }
           
          // Zielpfad normalisieren
            this.targetPath = this.targetPath.replaceAll("\\\\", "/");
            if ( !(this.targetPath.charAt(this.targetPath.length()-1)=='/') )
              this.targetPath += "/";
           
          } catch (IOException iox) {
           
            if ( file!=null ) {
              if ( file.isFile() )
                throw new Exception(file.getCanonicalPath() +": "+ iox.getMessage());
              else
                throw new Exception(this.targetPath +": "+ iox.getMessage());
            } else
              throw new Exception(this.targetPath +": "+ iox.getMessage());
          }
        }
      }
     
    // zus�tzlich f�r zip Manifest und Ziel-Archiv testen
      if ( this.zip || this.add ) {
       
        // Manifest �berpr�fen
        if ( this.manifestFile!=null ) {
          file = new File(this.manifestFile);
          if ( !file.canRead() )
            throw new Exception("manifest file "+file.getCanonicalPath()+" not found or not readable");
        }
       
        // jar-Archiv �berpr�fen
        file = new File(this.targetPath);
       
        if ( file.isDirectory() )
          throw new Exception( file.getCanonicalPath() + " is a directory");
      }
     
    } catch (Exception e) {
      this.loglnError("\n abort: "+e.getMessage());
      System.exit(-1);
    }
   
  // spezifizierte Aufgabe ausf�hren
    try {
     
      if ( this.check ) this.executeCheck(); else
      if ( this.unzip ) this.executeUnzip(); else
      if ( this.zip   ) this.executeZip(); else
      if ( this.add   ) this.executeAdd();
     
    } catch (Exception e) {
      this.loglnError("\n error: "+e.getMessage());
      System.exit(-1);
    }
   
    System.exit(0);
  }
 
  /**
   * @param args
   */
  public static void main(String[] args) {
   
    JarMerge jarmerge = new JarMerge(args);
    jarmerge.execute();
  }
}
TOP

Related Classes of sos.util.JarMerge

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.