1   /*
2    *  JarFileMerger.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Oana Hamza, 09/06/00
12   *
13   *  $Id: JarFiles.java,v 1.13 2001/03/09 13:02:11 cursu Exp $
14   */
15  
16  package gate.util;
17  
18  import java.util.*;
19  import java.util.jar.*;
20  import java.util.zip.*;
21  
22  import java.lang.*;
23  import java.io.*;
24  
25  /** This class is used to merge a set of Jar/Zip Files in a Jar File
26    * It is ignored the manifest.
27    */
28  public class JarFiles {
29  
30    /** Debug flag */
31    private static final boolean DEBUG = false;
32    private StringBuffer dbgString = new StringBuffer();
33    private boolean warning = false;
34    String buggyJar = null;
35  
36    private final static int BUFF_SIZE = 65000;
37  
38    private Set directorySet = null;
39  
40    private byte buffer[] = null;
41  
42    public JarFiles(){
43      directorySet = new HashSet();
44      buffer = new byte[BUFF_SIZE];
45    }
46  
47    /** This method takes the content of all jar/zip files from the set
48      * jarFileNames and put them in a file with the name outputFileName.
49      * If the jar entry is manifest then this information isn't added.
50      * @param jarFileNames is a set of names of files (jar/zip)
51      * @param outputFileName is the name of the file which contains all the
52      * classes of jarFilesNames
53      */
54    public void merge(Set jarFileNames, String destinationJarName)
55                                                        throws GateException {
56      String sourceJarName = null;
57      JarOutputStream jarFileDestination = null;
58      JarFile jarFileSource = null;
59  
60      try {
61        // create the output jar file
62        jarFileDestination =
63          new JarOutputStream(new FileOutputStream(destinationJarName));
64  
65        dbgString.append("Creating " + destinationJarName + " from these JARs:\n");
66        // iterate through the Jar files set
67        Iterator jarFileNamesIterator = jarFileNames.iterator();
68  
69        while (jarFileNamesIterator.hasNext()) {
70          sourceJarName = (String) jarFileNamesIterator.next();
71  
72          // create the new input jar files based on the file name
73          jarFileSource = new JarFile(sourceJarName);
74  
75          // Out.println("Adding " + sourceJarName + " to "
76          // + destinationJarName);
77          addJar(jarFileDestination, jarFileSource);
78          if (jarFileSource.getName().equals(buggyJar))
79            dbgString.append(sourceJarName + "...problems occured ! \n");
80          else
81            dbgString.append(sourceJarName + "...added OK ! \n");
82          jarFileSource.close();
83        }//End while
84  
85        jarFileDestination.close();
86  
87      } catch(IOException ioe) {
88        ioe.printStackTrace(Err.getPrintWriter());
89        //System.exit(1);
90      }
91      if (warning == true)
92          Out.prln(dbgString);
93    }// merge
94  
95  
96    /**
97      * This method adds all entries from sourceJar to destinationJar
98      * NOTE: that manifest information is not added, method will throw
99      * a gate Exception if a duplicate entry file is found.
100     * @param destinationJar the jar that will collect all the entries
101     * from source jar
102     * @param sourceJar doesn't need any explanation ... DOES it?
103     */
104   private void addJar(JarOutputStream destinationJar, JarFile sourceJar)
105                                                        throws GateException {
106     try {
107 
108       // get an enumeration of all entries from the sourceJar
109       Enumeration jarFileEntriesEnum = sourceJar.entries();
110 
111       JarEntry currentJarEntry = null;
112       while (jarFileEntriesEnum.hasMoreElements()) {
113 
114         // get a JarEntry
115         currentJarEntry = (JarEntry) jarFileEntriesEnum.nextElement();
116 
117         // if current entry is manifest then it is skipped
118         if(currentJarEntry.getName().equalsIgnoreCase("META-INF/") ||
119           currentJarEntry.getName().equalsIgnoreCase("META-INF/MANIFEST.MF"))
120           continue;
121 
122         // if current entry is a directory that was previously added to the
123         // destination JAR then it is skipped
124         if( currentJarEntry.isDirectory() &&
125             directorySet.contains(currentJarEntry.getName())
126            ) continue;
127 
128         // otherwise the current entry is added to the final jar file
129         try {
130           // if the entry is directory then is added to the directorySet
131           // NOTE: files entries are not added to this set
132           if (currentJarEntry.isDirectory())
133             directorySet.add(currentJarEntry.getName());
134 
135           // put the entry into the destination JAR
136           destinationJar.putNextEntry(new JarEntry(currentJarEntry.getName()));
137 
138           // add the binary data from the entry
139           // NOTE: if the entry is a directory there will be no binary data
140           // get an input stream from the entry
141           InputStream currentEntryStream =
142             sourceJar.getInputStream(currentJarEntry);
143 
144           // write data to destinationJar
145           int  bytesRead = 0;
146           while((bytesRead = currentEntryStream.read(buffer,0,BUFF_SIZE)) != -1)
147                 destinationJar.write(buffer,0,bytesRead);
148 
149           // close the input stream
150           currentEntryStream.close();
151 
152           // flush the destinationJar in order to be sure that
153           // everything is there
154           destinationJar.flush();
155 
156           // close the new added entry and  prepare to read and write
157           // another one
158           // NOTE: destinationJar.putNextEntry automaticaly closes any previous
159           // opened entry
160           destinationJar.closeEntry();
161 
162         } catch (java.util.zip.ZipException ze) {
163           if(!currentJarEntry.isDirectory()){
164             warning = true;
165             buggyJar = sourceJar.getName();
166             Out.prln("WARNING: Duplicate file entry " +
167               currentJarEntry.getName() + " (this file will be discarded)..." +
168               "It happened while adding " +
169               sourceJar.getName() +  " !\n");
170             dbgString.append(currentJarEntry.getName() +" file from " +
171                 sourceJar.getName() + " was discarded :( !\n");
172           }// End if
173         }
174       }// while(jarFileEntriesEnum.hasMoreElements())
175     } catch (java.io.IOException e) {
176       e.printStackTrace(Err.getPrintWriter());
177       // System.exit(1);
178     }
179   }// addJar
180 
181   /** args[0] is the final jar file and the other are the set of
182     * jar file names
183     * e.g. java gate.util.JarFiles libs.jar ../lib/*.jar ../lib/*.zip
184     * will create a file calls libs.jar which will contain all
185     * jar files and zip files
186     */
187 
188   public static void main(String[] args) {
189     if(args.length < 2) {
190                    Err.println("USAGE : JarFiles arg0 arg1 ... argN" +
191                                     "(must be at least 2 args)");
192                    //System.exit(1);
193     } else {
194       JarFiles jarFiles = new JarFiles();
195       Set filesToMerge = new HashSet();
196       for (int i=1; i<args.length; i++) {
197         filesToMerge.add(args[i]);
198     }
199     try {
200       jarFiles.merge(filesToMerge, args[0]);
201     } catch (GateException ge) {
202       ge.printStackTrace(Err.getPrintWriter());
203     }
204     }// if
205   }// main
206 
207 }// class JarFiles
208