View Javadoc
1   package org.codehaus.mojo.webstart.pack200;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.shared.utils.io.IOUtil;
23  import org.codehaus.plexus.component.annotations.Component;
24  
25  import java.io.BufferedInputStream;
26  import java.io.BufferedOutputStream;
27  import java.io.DataInputStream;
28  import java.io.File;
29  import java.io.FileFilter;
30  import java.io.FileInputStream;
31  import java.io.FileOutputStream;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.io.OutputStream;
35  import java.util.Collections;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.jar.JarFile;
40  import java.util.jar.JarOutputStream;
41  import java.util.jar.Pack200;
42  import java.util.jar.Pack200.Packer;
43  import java.util.zip.Deflater;
44  import java.util.zip.GZIPInputStream;
45  import java.util.zip.GZIPOutputStream;
46  
47  /**
48   * Default implementation of the {@link Pack200Tool}.
49   *
50   * @author tchemit <chemit@codelutin.com>
51   * @since 1.0-beta-2
52   */
53  @Component( role = Pack200Tool.class, hint = "default" )
54  public class DefaultPack200Tool
55      implements Pack200Tool
56  {
57  
58      public static final String PACK_GZ_EXTENSION = ".pack.gz";
59  
60      public static final String PACK_EXTENSION = ".pack";
61  
62      /**
63       * {@inheritDoc}
64       */
65      public void pack( File source, File destination, Map<String, String> props, boolean gzip )
66          throws IOException
67      {
68          JarFile jar = null;
69          OutputStream out = null;
70          try
71          {
72              out = new FileOutputStream( destination );
73              if ( gzip )
74              {
75                  out = new GZIPOutputStream( out )
76                  {
77                      {
78                          def.setLevel( Deflater.BEST_COMPRESSION );
79                      }
80                  };
81              }
82              out = new BufferedOutputStream( out );
83  
84              jar = new JarFile( source, false );
85  
86              Pack200.Packer packer = Pack200.newPacker();
87              packer.properties().putAll( props );
88              packer.pack( jar, out );
89          }
90          finally
91          {
92              IOUtil.close( out );
93              if ( jar != null )
94              {
95                  jar.close();
96              }
97          }
98      }
99  
100     /**
101      * {@inheritDoc}
102      */
103     public void repack( File source, File destination, Map<String, String> props )
104         throws IOException
105     {
106         File tempFile = new File( source.toString() + ".tmp" );
107 
108         try
109         {
110             pack( source, tempFile, props, false );
111             unpack( tempFile, destination, props );
112         }
113         finally
114         {
115             deleteFile( tempFile );
116         }
117     }
118 
119     /**
120      * {@inheritDoc}
121      */
122     public void unpack( File source, File destination, Map<String, String> props )
123         throws IOException
124     {
125         InputStream in = null;
126         JarOutputStream out = null;
127         try
128         {
129             in = new FileInputStream( source );
130             if ( isGzipped( source ) )
131             {
132                 in = new GZIPInputStream( in );
133             }
134             in = new BufferedInputStream( in );
135 
136             out = new JarOutputStream( new BufferedOutputStream( new FileOutputStream( destination ) ) );
137 
138             Pack200.Unpacker unpacker = Pack200.newUnpacker();
139             unpacker.properties().putAll( props );
140             unpacker.unpack( in, out );
141         }
142         finally
143         {
144             IOUtil.close( in );
145             IOUtil.close( out );
146         }
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     public void packJars( File directory, FileFilter jarFileFilter, boolean gzip, List<String> passFiles )
153         throws IOException
154     {
155         // getLog().debug( "packJars for " + directory );
156         File[] jarFiles = directory.listFiles( jarFileFilter );
157         for ( File jarFile1 : jarFiles )
158         {
159             // getLog().debug( "packJars: " + jarFiles[i] );
160 
161             final String extension = gzip ? PACK_GZ_EXTENSION : PACK_EXTENSION;
162 
163             File jarFile = jarFile1;
164 
165             File pack200Jar = new File( jarFile.getParentFile(), jarFile.getName() + extension );
166 
167             deleteFile( pack200Jar );
168 
169             Map<String, String> propMap = new HashMap<String, String>();
170             // Work around a JDK bug affecting large JAR files, see MWEBSTART-125
171             propMap.put( Pack200.Packer.SEGMENT_LIMIT, String.valueOf( -1 ) );
172 
173             // set passFiles if available
174             if ( passFiles != null && !passFiles.isEmpty() )
175             {
176                 for ( int j = 0; j < passFiles.size(); j++ )
177                 {
178                     propMap.put( Packer.PASS_FILE_PFX + j, passFiles.get( j ) );
179                 }
180             }
181 
182             pack( jarFile, pack200Jar, propMap, gzip );
183             setLastModified( pack200Jar, jarFile.lastModified() );
184         }
185     }
186 
187     /**
188      * {@inheritDoc}
189      */
190     public File packJar( File jarFile, boolean gzip, List<String> passFiles )
191         throws IOException
192     {
193         final String extension = gzip ? PACK_GZ_EXTENSION : PACK_EXTENSION;
194 
195         File pack200Jar = new File( jarFile.getParentFile(), jarFile.getName() + extension );
196 
197         deleteFile( pack200Jar );
198 
199         Map<String, String> propMap = new HashMap<String, String>();
200         // Work around a JDK bug affecting large JAR files, see MWEBSTART-125
201         propMap.put( Pack200.Packer.SEGMENT_LIMIT, String.valueOf( -1 ) );
202 
203         // set passFiles if available
204         if ( passFiles != null && !passFiles.isEmpty() )
205         {
206             for ( int j = 0; j < passFiles.size(); j++ )
207             {
208                 propMap.put( Packer.PASS_FILE_PFX + j, passFiles.get( j ) );
209             }
210         }
211 
212         pack( jarFile, pack200Jar, propMap, gzip );
213         setLastModified( pack200Jar, jarFile.lastModified() );
214         return pack200Jar;
215     }
216 
217 
218     /**
219      * {@inheritDoc}
220      */
221     public void unpackJars( File directory, FileFilter pack200FileFilter )
222         throws IOException
223     {
224         // getLog().debug( "unpackJars for " + directory );
225         File[] packFiles = directory.listFiles( pack200FileFilter );
226         for ( File packFile : packFiles )
227         {
228             final String packedJarPath = packFile.getAbsolutePath();
229             int extensionLength = packedJarPath.endsWith( PACK_GZ_EXTENSION ) ? 8 : 5;
230             String jarFileName = packedJarPath.substring( 0, packedJarPath.length() - extensionLength );
231             File jarFile = new File( jarFileName );
232 
233             deleteFile( jarFile );
234 
235             unpack( packFile, jarFile, Collections.<String, String>emptyMap() );
236             setLastModified( jarFile, packFile.lastModified() );
237         }
238     }
239 
240     /**
241      * {@inheritDoc}
242      */
243     public File unpackJar( File packFile )
244         throws IOException
245     {
246         final String packedJarPath = packFile.getAbsolutePath();
247         int extensionLength = packedJarPath.endsWith( PACK_GZ_EXTENSION ) ? 8 : 5;
248         String jarFileName = packedJarPath.substring( 0, packedJarPath.length() - extensionLength );
249         File jarFile = new File( jarFileName );
250 
251         deleteFile( jarFile );
252 
253         unpack( packFile, jarFile, Collections.<String, String>emptyMap() );
254         setLastModified( jarFile, packFile.lastModified() );
255         return jarFile;
256     }
257 
258     private void deleteFile( File file )
259         throws IOException
260     {
261         if ( file.exists() )
262         {
263             boolean delete = file.delete();
264             if ( !delete )
265             {
266                 throw new IOException( "Could not delete file " + file );
267             }
268         }
269     }
270 
271     private void setLastModified( File file, long modifi )
272         throws IOException
273     {
274         boolean b = file.setLastModified( modifi );
275         if ( !b )
276         {
277             throw new IOException( "Could not change last modifified on file: " + file );
278         }
279     }
280 
281     /**
282      * Tells if the specified file is gzipped.
283      *
284      * @param file the file to test
285      */
286     private static boolean isGzipped( File file )
287         throws IOException
288     {
289         DataInputStream is = new DataInputStream( new FileInputStream( file ) );
290         int i = is.readInt();
291         is.close();
292         return ( i & 0xffffff00 ) == 0x1f8b0800;
293     }
294 }