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