View Javadoc
1   package org.apache.maven.shared.utils;
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 java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.OutputStream;
28  import java.util.Date;
29  import java.util.zip.ZipEntry;
30  import java.util.zip.ZipInputStream;
31  
32  import org.apache.maven.shared.utils.io.FileUtils;
33  import org.apache.maven.shared.utils.io.IOUtil;
34  
35  /**
36   * Expand will unpack the given zip archive.
37   *
38   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
39   */
40  class Expand
41  {
42      /**
43       * Source file which should get expanded
44       */
45      private File source;
46  
47      /**
48       * destination directory
49       */
50      private File dest;
51  
52      /**
53       * if the unpackaging should get performed if the destination already exists.
54       */
55      private boolean overwrite = false;
56  
57      private static final int BUFFER_SIZE = 2 ^ 16;
58  
59  
60      /**
61       * The zip archive which should get expanded.
62       *
63       * @param sourceArchive
64       */
65      public void setSrc( File sourceArchive )
66      {
67          this.source = sourceArchive;
68      }
69  
70      /**
71       * Set the destination directory into which the archive should get expanded.
72       * The directory will get created if it doesn't yet exist
73       * while executing the expand.
74       *
75       * @param destinationDirectory
76       */
77      public void setDest( File destinationDirectory )
78      {
79          this.dest = destinationDirectory;
80      }
81  
82      /**
83       * If the destination directory should get overwritten if the content
84       * already exists. If <code>false</code> we will only overwrite if the local
85       * file or directory is older than the one in the archive.
86       *
87       * @param overwrite
88       */
89      public void setOverwrite( boolean overwrite )
90      {
91          this.overwrite = overwrite;
92      }
93  
94      /**
95       * Actually perform the unpacking of the source archive
96       * into the destination directory.
97       *
98       * @throws Exception
99       */
100     public void execute()
101         throws Exception
102     {
103         expandFile( source, dest );
104     }
105 
106     /**
107      * <p>It is intended to be overwritten when implementing an own unarchiver</p>
108      * <p/>
109      * <p><b>Note:</b> we kept this protected method for the sake of backward compatibility!</p>
110      *
111      * @param srcFile The source file.
112      * @param destination The destination.
113      * @throws Exception In case of failure.
114      */
115     void expandFile( File srcFile, File destination )
116         throws Exception
117     {
118         if ( source == null )
119         {
120             throw new NullPointerException( "Source Archive must not be null!" );
121         }
122 
123         File destDir = destination;
124         if ( destDir == null )
125         {
126             destDir = new File( System.getProperty( "user.dir" ) );
127         }
128 
129         ZipInputStream in = null;
130         try
131         {
132             in = new ZipInputStream( new FileInputStream( srcFile ) );
133 
134             for ( ZipEntry zipEntry = in.getNextEntry(); zipEntry != null; zipEntry = in.getNextEntry() )
135             {
136                 String zipEntryName = zipEntry.getName();
137                 Date zipEntryDate = new Date( zipEntry.getTime() );
138 
139                 extractFile( source, destDir, in, zipEntryName, zipEntryDate, zipEntry.isDirectory() );
140             }
141 
142             in.close();
143             in = null;
144         }
145         finally
146         {
147             IOUtil.close( in );
148         }
149     }
150 
151     /**
152      * Extract a single ZipEntry.
153      * <p/>
154      * <p><b>Note:</b> we kept this protected method for the sake of backward compatibility!</p>
155      *
156      * @param archive               the archive to unpack
157      * @param destDir               the destination dirctory
158      * @param compressedInputStream
159      * @param entryName
160      * @param entryDate
161      * @param isDirectory
162      * @throws Exception
163      */
164     void extractFile( File archive, File destDir, InputStream compressedInputStream, String entryName,
165                                 Date entryDate, boolean isDirectory )
166         throws Exception
167     {
168         File targetFile = new File( destDir, entryName );
169 
170         if ( !targetFile.getAbsolutePath().startsWith( destDir.getAbsolutePath() ) )
171         {
172             throw new IOException( "Entry '" + entryName + "' outside the target directory." );
173         }
174 
175         // if overwrite is specified and the file type
176         // of the existing file does not match, then delete it
177         if ( overwrite && targetFile.exists() && targetFile.isDirectory() != isDirectory )
178         {
179             deleteFileOrDir( targetFile );
180         }
181 
182         if ( !targetFile.exists() || overwrite || targetFile.lastModified() <= entryDate.getTime() )
183         {
184             if ( isDirectory )
185             {
186                 targetFile.mkdirs();
187             }
188             else
189             {
190                 byte[] buffer = new byte[BUFFER_SIZE];
191                 OutputStream out = null;
192                 try
193                 {
194                     out = new FileOutputStream( targetFile );
195 
196                     int len;
197                     while ( ( len = compressedInputStream.read( buffer ) ) >= 0 )
198                     {
199                         out.write( buffer, 0, len );
200                     }
201 
202                     out.close();
203                     out = null;
204                 }
205                 finally
206                 {
207                     IOUtil.close( out );
208                 }
209                 targetFile.setLastModified( entryDate.getTime() );
210             }
211         }
212     }
213 
214     /**
215      * small helper method who deletes the given directory or file.
216      *
217      * @param targetFile
218      * @throws IOException
219      */
220     private void deleteFileOrDir( File targetFile )
221         throws IOException
222     {
223         if ( targetFile.isDirectory() )
224         {
225             FileUtils.deleteDirectory( targetFile );
226         }
227         else
228         {
229             FileUtils.delete( targetFile );
230         }
231 
232     }
233 }