JarFiles.java
001 /*
002  *  JarFileMerger.java
003  *
004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Oana Hamza, 09/06/00
013  *
014  *  $Id: JarFiles.java 12006 2009-12-01 17:24:28Z thomas_heitz $
015  */
016 
017 package gate.util;
018 
019 import java.io.*;
020 import java.util.*;
021 import java.util.jar.*;
022 
023 /** This class is used to merge a set of Jar/Zip Files in a Jar File
024   * It is ignored the manifest.
025   */
026 public class JarFiles {
027 
028   /** Debug flag */
029   private static final boolean DEBUG = false;
030   private StringBuffer dbgString = new StringBuffer();
031   private boolean warning = false;
032   String buggyJar = null;
033 
034   private final static int BUFF_SIZE = 65000;
035 
036   private Set directorySet = null;
037 
038   private byte buffer[] null;
039 
040   public JarFiles(){
041     directorySet = new HashSet();
042     buffer = new byte[BUFF_SIZE];
043   }
044 
045   /** This method takes the content of all jar/zip files from the set
046     * jarFileNames and put them in a file with the name outputFileName.
047     * If the jar entry is manifest then this information isn't added.
048     @param jarFileNames is a set of names of files (jar/zip)
049     @param destinationJarName is the name of the file which contains all the
050     * classes of jarFilesNames
051     */
052   public void merge(Set jarFileNames, String destinationJarName)
053                                                       throws GateException {
054     String sourceJarName = null;
055     JarOutputStream jarFileDestination = null;
056     JarFile jarFileSource = null;
057 
058     try {
059       // create the output jar file
060       jarFileDestination =
061         new JarOutputStream(new FileOutputStream(destinationJarName));
062 
063       dbgString.append("Creating " + destinationJarName + " from these JARs:\n");
064       // iterate through the Jar files set
065       Iterator jarFileNamesIterator = jarFileNames.iterator();
066 
067       while (jarFileNamesIterator.hasNext()) {
068         sourceJarName = (StringjarFileNamesIterator.next();
069 
070         // create the new input jar files based on the file name
071         jarFileSource = new JarFile(sourceJarName);
072 
073         // Out.println("Adding " + sourceJarName + " to "
074         // + destinationJarName);
075         addJar(jarFileDestination, jarFileSource);
076         if (jarFileSource.getName().equals(buggyJar))
077           dbgString.append(sourceJarName + "...problems occured ! \n");
078         else
079           dbgString.append(sourceJarName + "...added OK ! \n");
080         jarFileSource.close();
081       }//End while
082 
083       jarFileDestination.close();
084 
085     catch(IOException ioe) {
086       ioe.printStackTrace(Err.getPrintWriter());
087       //System.exit(1);
088     }
089     if (warning == true)
090         Out.prln(dbgString);
091   }// merge
092 
093 
094   /**
095     * This method adds all entries from sourceJar to destinationJar
096     * NOTE: that manifest information is not added, method will throw
097     * a gate Exception if a duplicate entry file is found.
098     @param destinationJar the jar that will collect all the entries
099     * from source jar
100     @param sourceJar doesn't need any explanation ... DOES it?
101     */
102   private void addJar(JarOutputStream destinationJar, JarFile sourceJar)
103                                                        throws GateException {
104     try {
105 
106       // get an enumeration of all entries from the sourceJar
107       Enumeration jarFileEntriesEnum = sourceJar.entries();
108 
109       JarEntry currentJarEntry = null;
110       while (jarFileEntriesEnum.hasMoreElements()) {
111 
112         // get a JarEntry
113         currentJarEntry = (JarEntryjarFileEntriesEnum.nextElement();
114 
115         // if current entry is manifest then it is skipped
116         if(currentJarEntry.getName().equalsIgnoreCase("META-INF/"||
117           currentJarEntry.getName().equalsIgnoreCase("META-INF/MANIFEST.MF"))
118           continue;
119 
120         // if current entry is a directory that was previously added to the
121         // destination JAR then it is skipped
122         ifcurrentJarEntry.isDirectory() &&
123             directorySet.contains(currentJarEntry.getName())
124            continue;
125 
126         // otherwise the current entry is added to the final jar file
127         try {
128           // if the entry is directory then is added to the directorySet
129           // NOTE: files entries are not added to this set
130           if (currentJarEntry.isDirectory())
131             directorySet.add(currentJarEntry.getName());
132 
133           // put the entry into the destination JAR
134           destinationJar.putNextEntry(new JarEntry(currentJarEntry.getName()));
135 
136           // add the binary data from the entry
137           // NOTE: if the entry is a directory there will be no binary data
138           // get an input stream from the entry
139           InputStream currentEntryStream =
140             sourceJar.getInputStream(currentJarEntry);
141 
142           // write data to destinationJar
143           int  bytesRead = 0;
144           while((bytesRead = currentEntryStream.read(buffer,0,BUFF_SIZE)) != -1)
145                 destinationJar.write(buffer,0,bytesRead);
146 
147           // close the input stream
148           currentEntryStream.close();
149 
150           // flush the destinationJar in order to be sure that
151           // everything is there
152           destinationJar.flush();
153 
154           // close the new added entry and  prepare to read and write
155           // another one
156           // NOTE: destinationJar.putNextEntry automaticaly closes any previous
157           // opened entry
158           destinationJar.closeEntry();
159 
160         catch (java.util.zip.ZipException ze) {
161           if(!currentJarEntry.isDirectory()){
162             warning = true;
163             buggyJar = sourceJar.getName();
164             Out.prln("WARNING: Duplicate file entry " +
165               currentJarEntry.getName() " (this file will be discarded)..." +
166               "It happened while adding " +
167               sourceJar.getName() +  " !\n");
168             dbgString.append(currentJarEntry.getName() +" file from " +
169                 sourceJar.getName() " was discarded :( !\n");
170           }// End if
171         }
172       }// while(jarFileEntriesEnum.hasMoreElements())
173     catch (java.io.IOException e) {
174       e.printStackTrace(Err.getPrintWriter());
175       // System.exit(1);
176     }
177   }// addJar
178 
179   /** args[0] is the final jar file and the other are the set of
180     * jar file names
181     * e.g. java gate.util.JarFiles libs.jar ../lib/*.jar ../lib/*.zip
182     * will create a file calls libs.jar which will contain all
183     * jar files and zip files
184     */
185 
186   public static void main(String[] args) {
187     if(args.length < 2) {
188                    Err.println("USAGE : JarFiles arg0 arg1 ... argN" +
189                                     "(must be at least 2 args)");
190                    //System.exit(1);
191     else {
192       JarFiles jarFiles = new JarFiles();
193       Set filesToMerge = new HashSet();
194       for (int i=1; i<args.length; i++) {
195         filesToMerge.add(args[i]);
196     }
197     try {
198       jarFiles.merge(filesToMerge, args[0]);
199     catch (GateException ge) {
200       ge.printStackTrace(Err.getPrintWriter());
201     }
202     }// if
203   }// main
204 
205 }// class JarFiles