KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > util > file > JarUtils


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.util.file;
23
24 import java.io.BufferedInputStream JavaDoc;
25 import java.io.BufferedOutputStream JavaDoc;
26 import java.io.File JavaDoc;
27 import java.io.FileFilter JavaDoc;
28 import java.io.FileInputStream JavaDoc;
29 import java.io.FileNotFoundException JavaDoc;
30 import java.io.FileOutputStream JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.io.InputStream JavaDoc;
33 import java.io.OutputStream JavaDoc;
34 import java.net.JarURLConnection JavaDoc;
35 import java.net.URL JavaDoc;
36 import java.net.URLConnection JavaDoc;
37 import java.util.jar.JarInputStream JavaDoc;
38 import java.util.jar.JarOutputStream JavaDoc;
39 import java.util.jar.Manifest JavaDoc;
40 import java.util.zip.ZipEntry JavaDoc;
41
42 /** A utility class for dealing with Jar files.
43
44 @author Scott.Stark@jboss.org
45 @version $Revision: 1958 $
46 */

47 public final class JarUtils
48 {
49    /**
50     * Hide the constructor
51     */

52    private JarUtils()
53    {
54    }
55    
56    /**
57     * <P>This function will create a Jar archive containing the src
58     * file/directory. The archive will be written to the specified
59     * OutputStream.</P>
60     *
61     * <P>This is a shortcut for<br>
62     * <code>jar(out, new File[] { src }, null, null, null);</code></P>
63     *
64     * @param out The output stream to which the generated Jar archive is
65     * written.
66     * @param src The file or directory to jar up. Directories will be
67     * processed recursively.
68     */

69    public static void jar(OutputStream JavaDoc out, File JavaDoc src) throws IOException JavaDoc
70    {
71       jar(out, new File JavaDoc[] { src }, null, null, null);
72    }
73  
74    /**
75     * <P>This function will create a Jar archive containing the src
76     * file/directory. The archive will be written to the specified
77     * OutputStream.</P>
78     *
79     * <P>This is a shortcut for<br>
80     * <code>jar(out, src, null, null, null);</code></P>
81     *
82     * @param out The output stream to which the generated Jar archive is
83     * written.
84     * @param src The file or directory to jar up. Directories will be
85     * processed recursively.
86     */

87    public static void jar(OutputStream JavaDoc out, File JavaDoc[] src) throws IOException JavaDoc
88    {
89       jar(out, src, null, null, null);
90    }
91    
92    /**
93     * <P>This function will create a Jar archive containing the src
94     * file/directory. The archive will be written to the specified
95     * OutputStream. Directories are processed recursively, applying the
96     * specified filter if it exists.
97     *
98     * <P>This is a shortcut for<br>
99     * <code>jar(out, src, filter, null, null);</code></P>
100     *
101     * @param out The output stream to which the generated Jar archive is
102     * written.
103     * @param src The file or directory to jar up. Directories will be
104     * processed recursively.
105     * @param filter The filter to use while processing directories. Only
106     * those files matching will be included in the jar archive. If
107     * null, then all files are included.
108     */

109    public static void jar(OutputStream JavaDoc out, File JavaDoc[] src, FileFilter JavaDoc filter)
110       throws IOException JavaDoc
111    {
112       jar(out, src, filter, null, null);
113    }
114    
115    /**
116     * <P>This function will create a Jar archive containing the src
117     * file/directory. The archive will be written to the specified
118     * OutputStream. Directories are processed recursively, applying the
119     * specified filter if it exists.
120     *
121     * @param out The output stream to which the generated Jar archive is
122     * written.
123     * @param src The file or directory to jar up. Directories will be
124     * processed recursively.
125     * @param filter The filter to use while processing directories. Only
126     * those files matching will be included in the jar archive. If
127     * null, then all files are included.
128     * @param prefix The name of an arbitrary directory that will precede all
129     * entries in the jar archive. If null, then no prefix will be
130     * used.
131     * @param man The manifest to use for the Jar archive. If null, then no
132     * manifest will be included.
133     */

134    public static void jar(OutputStream JavaDoc out, File JavaDoc[] src, FileFilter JavaDoc filter,
135       String JavaDoc prefix, Manifest JavaDoc man) throws IOException JavaDoc
136    {
137       
138       for (int i = 0; i < src.length; i++)
139       {
140          if (!src[i].exists())
141          {
142             throw new FileNotFoundException JavaDoc(src.toString());
143          }
144       }
145       
146       JarOutputStream JavaDoc jout;
147       if (man == null)
148       {
149          jout = new JarOutputStream JavaDoc(out);
150       }
151       else
152       {
153          jout = new JarOutputStream JavaDoc(out, man);
154       }
155       if (prefix != null && prefix.length() > 0 && !prefix.equals("/"))
156       {
157          // strip leading '/'
158
if (prefix.charAt(0) == '/')
159          {
160             prefix = prefix.substring(1);
161          }
162          // ensure trailing '/'
163
if (prefix.charAt(prefix.length() - 1) != '/')
164          {
165             prefix = prefix + "/";
166          }
167       }
168       else
169       {
170          prefix = "";
171       }
172       JarInfo info = new JarInfo(jout, filter);
173       for (int i = 0; i < src.length; i++)
174       {
175          jar(src[i], prefix, info);
176       }
177       jout.close();
178    }
179    
180    /**
181     * This simple convenience class is used by the jar method to reduce the
182     * number of arguments needed. It holds all non-changing attributes
183     * needed for the recursive jar method.
184     */

185    private static class JarInfo
186    {
187       public JarOutputStream JavaDoc out;
188       public FileFilter JavaDoc filter;
189       public byte[] buffer;
190       
191       public JarInfo(JarOutputStream JavaDoc out, FileFilter JavaDoc filter)
192       {
193          this.out = out;
194          this.filter = filter;
195          buffer = new byte[1024];
196       }
197    }
198    
199    /**
200     * This recursive method writes all matching files and directories to
201     * the jar output stream.
202     */

203    private static void jar(File JavaDoc src, String JavaDoc prefix, JarInfo info)
204       throws IOException JavaDoc
205    {
206       
207       JarOutputStream JavaDoc jout = info.out;
208       if (src.isDirectory())
209       {
210          // create / init the zip entry
211
prefix = prefix + src.getName() + "/";
212          ZipEntry JavaDoc entry = new ZipEntry JavaDoc(prefix);
213          entry.setTime(src.lastModified());
214          entry.setMethod(JarOutputStream.STORED);
215          entry.setSize(0L);
216          entry.setCrc(0L);
217          jout.putNextEntry(entry);
218          jout.closeEntry();
219          
220          // process the sub-directories
221
File JavaDoc[] files = src.listFiles(info.filter);
222          for (int i = 0; i < files.length; i++)
223          {
224             jar(files[i], prefix, info);
225          }
226       }
227       else if (src.isFile())
228       {
229          // get the required info objects
230
byte[] buffer = info.buffer;
231          
232          // create / init the zip entry
233
ZipEntry JavaDoc entry = new ZipEntry JavaDoc(prefix + src.getName());
234          entry.setTime(src.lastModified());
235          jout.putNextEntry(entry);
236          
237          // dump the file
238
FileInputStream JavaDoc in = new FileInputStream JavaDoc(src);
239          int len;
240          while ((len = in.read(buffer, 0, buffer.length)) != -1)
241          {
242             jout.write(buffer, 0, len);
243          }
244          in.close();
245          jout.closeEntry();
246       }
247    }
248    
249    /**
250     * Dump the contents of a JarArchive to the dpecified destination.
251     */

252    public static void unjar(InputStream JavaDoc in, File JavaDoc dest) throws IOException JavaDoc
253    {
254       if (!dest.exists())
255       {
256          dest.mkdirs();
257       }
258       if (!dest.isDirectory())
259       {
260          throw new IOException JavaDoc("Destination must be a directory.");
261       }
262       JarInputStream JavaDoc jin = new JarInputStream JavaDoc(in);
263       byte[] buffer = new byte[1024];
264       
265       ZipEntry JavaDoc entry = jin.getNextEntry();
266       while (entry != null)
267       {
268          String JavaDoc fileName = entry.getName();
269          if (fileName.charAt(fileName.length() - 1) == '/')
270          {
271             fileName = fileName.substring(0, fileName.length() - 1);
272          }
273          if (fileName.charAt(0) == '/')
274          {
275             fileName = fileName.substring(1);
276          }
277          if (File.separatorChar != '/')
278          {
279             fileName = fileName.replace('/', File.separatorChar);
280          }
281          File JavaDoc file = new File JavaDoc(dest, fileName);
282          if (entry.isDirectory())
283          {
284             // make sure the directory exists
285
file.mkdirs();
286             jin.closeEntry();
287          }
288          else
289          {
290             // make sure the directory exists
291
File JavaDoc parent = file.getParentFile();
292             if (parent != null && !parent.exists())
293             {
294                parent.mkdirs();
295             }
296             
297             // dump the file
298
OutputStream JavaDoc out = new FileOutputStream JavaDoc(file);
299             int len = 0;
300             while ((len = jin.read(buffer, 0, buffer.length)) != -1)
301             {
302                out.write(buffer, 0, len);
303             }
304             out.flush();
305             out.close();
306             jin.closeEntry();
307             file.setLastModified(entry.getTime());
308          }
309          entry = jin.getNextEntry();
310       }
311       /* Explicity write out the META-INF/MANIFEST.MF so that any headers such
312       as the Class-Path are see for the unpackaged jar
313       */

314       Manifest JavaDoc mf = jin.getManifest();
315       if (mf != null)
316       {
317          File JavaDoc file = new File JavaDoc(dest, "META-INF/MANIFEST.MF");
318          File JavaDoc parent = file.getParentFile();
319          if( parent.exists() == false )
320          {
321             parent.mkdirs();
322          }
323          OutputStream JavaDoc out = new FileOutputStream JavaDoc(file);
324          mf.write(out);
325          out.flush();
326          out.close();
327       }
328       jin.close();
329    }
330
331    /** Given a URL check if its a jar url(jar:<url>!/archive) and if it is,
332     extract the archive entry into the given dest directory and return a file
333     URL to its location. If jarURL is not a jar url then it is simply returned
334     as the URL for the jar.
335
336     @param jarURL the URL to validate and extract the referenced entry if its
337       a jar protocol URL
338     @param dest the directory into which the nested jar will be extracted.
339     @return the file: URL for the jar referenced by the jarURL parameter.
340     */

341    public static URL JavaDoc extractNestedJar(URL JavaDoc jarURL, File JavaDoc dest)
342       throws IOException JavaDoc
343    {
344       // This may not be a jar URL so validate the protocol
345
if( jarURL.getProtocol().equals("jar") == false )
346          return jarURL;
347
348       String JavaDoc destPath = dest.getAbsolutePath();
349       URLConnection JavaDoc urlConn = jarURL.openConnection();
350       JarURLConnection JavaDoc jarConn = (JarURLConnection JavaDoc) urlConn;
351       // Extract the archive to dest/jarName-contents/archive
352
String JavaDoc parentArchiveName = jarConn.getJarFile().getName();
353       // Find the longest common prefix between destPath and parentArchiveName
354
int length = Math.min(destPath.length(), parentArchiveName.length());
355       int n = 0;
356       while( n < length )
357       {
358          char a = destPath.charAt(n);
359          char b = parentArchiveName.charAt(n);
360          if( a != b )
361             break;
362          n ++;
363       }
364       // Remove any common prefix from parentArchiveName
365
parentArchiveName = parentArchiveName.substring(n);
366
367       File JavaDoc archiveDir = new File JavaDoc(dest, parentArchiveName+"-contents");
368       if( archiveDir.exists() == false && archiveDir.mkdirs() == false )
369          throw new IOException JavaDoc("Failed to create contents directory for archive, path="+archiveDir.getAbsolutePath());
370       String JavaDoc archiveName = jarConn.getEntryName();
371       File JavaDoc archiveFile = new File JavaDoc(archiveDir, archiveName);
372       File JavaDoc archiveParentDir = archiveFile.getParentFile();
373       if( archiveParentDir.exists() == false && archiveParentDir.mkdirs() == false )
374          throw new IOException JavaDoc("Failed to create parent directory for archive, path="+archiveParentDir.getAbsolutePath());
375       InputStream JavaDoc archiveIS = jarConn.getInputStream();
376       FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc(archiveFile);
377       BufferedOutputStream JavaDoc bos = new BufferedOutputStream JavaDoc(fos);
378       byte[] buffer = new byte[4096];
379       int read;
380       while( (read = archiveIS.read(buffer)) > 0 )
381       {
382          bos.write(buffer, 0, read);
383       }
384       archiveIS.close();
385       bos.close();
386
387       // Return the file url to the extracted jar
388
return archiveFile.toURL();
389    }
390
391
392    /**
393     * A simple jar-like tool used for testing. It's actually faster than
394     * jar, though doesn't sipport as many options.
395     */

396    public static void main(String JavaDoc[] args) throws Exception JavaDoc
397    {
398       if (args.length == 0)
399       {
400          System.out.println("usage: <x or c> <jar-archive> <files...>");
401          System.exit(0);
402       }
403       if (args[0].equals("x"))
404       {
405          BufferedInputStream JavaDoc in = new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(args[1]));
406          File JavaDoc dest = new File JavaDoc(args[2]);
407          unjar(in, dest);
408       }
409       else if (args[0].equals("c"))
410       {
411          BufferedOutputStream JavaDoc out = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(args[1]));
412          File JavaDoc[] src = new File JavaDoc[args.length - 2];
413          for (int i = 0; i < src.length; i++)
414          {
415             src[i] = new File JavaDoc(args[2 + i]);
416          }
417          jar(out, src);
418       }
419       else
420       {
421          System.out.println("Need x or c as first argument");
422       }
423    }
424 }
425
Popular Tags