View Javadoc
1   /*
2    * (C) Copyright 2011-2014 Nuxeo SA (http://nuxeo.com/) and contributors.
3    *
4    * All rights reserved. This program and the accompanying materials
5    * are made available under the terms of the GNU Lesser General Public License
6    * (LGPL) version 2.1 which accompanies this distribution, and is available at
7    * http://www.gnu.org/licenses/lgpl-2.1.html
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   * Lesser General Public License for more details.
13   *
14   * Contributors:
15   *     Julien Carsique
16   *
17   */
18  
19  package org.nuxeo.build.ant;
20  
21  import java.io.File;
22  import java.io.FileWriter;
23  import java.io.IOException;
24  import java.util.Enumeration;
25  import java.util.Iterator;
26  import java.util.LinkedHashSet;
27  import java.util.Set;
28  import java.util.zip.ZipEntry;
29  import java.util.zip.ZipFile;
30  
31  import org.apache.commons.io.IOUtils;
32  import org.apache.tools.ant.BuildException;
33  import org.apache.tools.ant.Project;
34  import org.apache.tools.ant.Task;
35  import org.apache.tools.ant.types.PatternSet;
36  import org.apache.tools.ant.types.PatternSet.NameEntry;
37  
38  /**
39   * Compares two ZIP files, generating a {@link PatternSet}
40   */
41  public class ZipDiffTask extends Task {
42  
43      public static String newline = System.getProperty("line.separator");
44  
45      protected File file1;
46  
47      protected File file2;
48  
49      protected File includesfile;
50  
51      protected File excludesfile;
52  
53      private FileWriter fileWriter;
54  
55      private PatternSet patternSet;
56  
57      private boolean ignoreContent = false;
58  
59      private String ignoreContentPattern = ".*(?<!SNAPSHOT)\\.jar$";
60  
61      public void setFile1(File file1) {
62          this.file1 = file1;
63      }
64  
65      public void setFile2(File file2) {
66          this.file2 = file2;
67      }
68  
69      public void setIncludesfile(File includesfile) {
70          this.includesfile = includesfile;
71      }
72  
73      public void setExcludesfile(File excludesfile) {
74          this.excludesfile = excludesfile;
75      }
76  
77      public void setPatternsetid(String id) {
78          patternSet = new PatternSet();
79          getProject().addReference(id, patternSet);
80      }
81  
82      /**
83       * If two files have same name, their content is compared. This option
84       * allows to bypass the content check.
85       *
86       * @param ignoreContent if true, files' content is not checked
87       * @since 1.13
88       */
89      public void setIgnoreContent(boolean ignoreContent) {
90          this.ignoreContent = ignoreContent;
91      }
92  
93      /**
94       * If two files have same name, their content is compared. This option
95       * allows to bypass the content check for filenames matching the given
96       * pattern.
97       *
98       * @param ignoreContentPattern Pattern of filenames for which content must
99       *            not be checked. Default value bypass content check for JAR
100      *            files but SNAPSHOT ones.
101      * @since 1.13
102      */
103     public void setIgnoreContentPattern(String ignoreContentPattern) {
104         this.ignoreContentPattern = ignoreContentPattern;
105     }
106 
107     @Override
108     public void execute() throws BuildException {
109         ZipFile zipfile1 = null;
110         ZipFile zipfile2 = null;
111         try {
112             zipfile1 = new ZipFile(file1);
113             zipfile2 = new ZipFile(file2);
114 
115             Set<String> set1 = new LinkedHashSet<>();
116             for (Enumeration<? extends ZipEntry> zipEntries = zipfile1.entries(); zipEntries.hasMoreElements();) {
117                 set1.add((zipEntries.nextElement()).getName());
118             }
119             Set<String> set2 = new LinkedHashSet<>();
120             for (Enumeration<? extends ZipEntry> zipEntries = zipfile2.entries(); zipEntries.hasMoreElements();) {
121                 set2.add((zipEntries.nextElement()).getName());
122             }
123 
124             try {
125                 if (includesfile != null) {
126                     includesfile.createNewFile();
127                     fileWriter = new FileWriter(includesfile);
128                 }
129 
130                 // includes (files from file1 not present or differ in file2)
131                 for (Iterator<String> i = set1.iterator(); i.hasNext();) {
132                     String filename = i.next();
133                     if (!set2.contains(filename)) {
134                         log("Only in " + file1.getName() + ": " + filename,
135                                 Project.MSG_INFO);
136                         include(filename, fileWriter);
137                         continue;
138                     }
139                     set2.remove(filename);
140                     if (!ignoreContent
141                             && !filename.matches(ignoreContentPattern)) {
142                         try {
143                             if (!IOUtils.contentEquals(
144                                     zipfile1.getInputStream(zipfile1.getEntry(filename)),
145                                     zipfile2.getInputStream(zipfile2.getEntry(filename)))) {
146                                 log("Content differs: " + filename,
147                                         Project.MSG_INFO);
148                                 include(filename, fileWriter);
149                             }
150                         } catch (IOException e) {
151                             log(e, Project.MSG_WARN);
152                         }
153                     }
154                 }
155             } catch (IOException e) {
156                 throw new BuildException(e);
157             } finally {
158                 IOUtils.closeQuietly(fileWriter);
159             }
160 
161             // excludes (files from file2 not present in file1)
162             try {
163                 if (excludesfile != null) {
164                     excludesfile.createNewFile();
165                     fileWriter = new FileWriter(excludesfile);
166                 }
167                 for (Iterator<String> i = set2.iterator(); i.hasNext();) {
168                     String filename = i.next();
169                     log("Only in " + file2.getName() + ": " + filename,
170                             Project.MSG_INFO);
171                     exclude(filename, fileWriter);
172                 }
173             } catch (IOException e) {
174                 throw new BuildException(e);
175             } finally {
176                 IOUtils.closeQuietly(fileWriter);
177             }
178         } catch (IOException e) {
179             throw new BuildException("Error opening " + file1 + " or " + file2,
180                     e);
181         } finally {
182             if (zipfile1 != null) {
183                 try {
184                     zipfile1.close();
185                 } catch (IOException e) {
186                     throw new BuildException(e);
187                 }
188             }
189             if (zipfile2 != null) {
190                 try {
191                     zipfile2.close();
192                 } catch (IOException e) {
193                     throw new BuildException(e);
194                 }
195             }
196         }
197     }
198 
199     protected void exclude(String filename, FileWriter writer)
200             throws IOException {
201         if (patternSet != null) {
202             NameEntry exclude = patternSet.createExclude();
203             exclude.setName(filename);
204         }
205         write(filename, writer);
206     }
207 
208     protected void include(String filename, FileWriter writer)
209             throws IOException {
210         if (patternSet != null) {
211             NameEntry include = patternSet.createInclude();
212             include.setName(filename);
213         }
214         write(filename, writer);
215     }
216 
217     private void write(String filename, FileWriter writer) throws IOException {
218         if (writer != null) {
219             writer.write(filename + newline);
220         }
221     }
222 }