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