1 package com.atlassian.plugin.util.zip;
2
3 import org.apache.commons.io.FilenameUtils;
4 import org.apache.commons.io.IOUtils;
5 import org.apache.commons.io.FileUtils;
6 import org.apache.commons.lang.StringUtils;
7 import org.slf4j.LoggerFactory;
8 import org.slf4j.Logger;
9
10 import java.io.*;
11 import java.util.zip.ZipEntry;
12 import java.util.zip.ZipInputStream;
13 import java.util.*;
14
15 import com.google.common.annotations.VisibleForTesting;
16
17 public abstract class AbstractUnzipper implements Unzipper
18 {
19 protected static Logger log = LoggerFactory.getLogger(FileUnzipper.class);
20 protected File destDir;
21
22 protected File saveEntry(InputStream is, ZipEntry entry) throws IOException
23 {
24 final File file = new File(destDir, normaliseAndVerify(entry.getName()));
25
26 if (entry.isDirectory())
27 {
28 file.mkdirs();
29 }
30 else
31 {
32 final File dir = new File(file.getParent());
33 dir.mkdirs();
34
35 FileOutputStream fos = null;
36 try
37 {
38 fos = new FileOutputStream(file);
39 IOUtils.copy(is, fos);
40 fos.flush();
41 }
42 catch (FileNotFoundException fnfe)
43 {
44 log.error("Error extracting a file to '" + destDir + File.separator + entry.getName() + "'. This destination is invalid for writing an extracted file stream to. ");
45 return null;
46 }
47 finally
48 {
49 IOUtils.closeQuietly(fos);
50 }
51 }
52 file.setLastModified(entry.getTime());
53
54 return file;
55 }
56
57 protected ZipEntry[] entries(ZipInputStream zis) throws IOException
58 {
59 List entries = new ArrayList();
60 try
61 {
62 ZipEntry zipEntry = zis.getNextEntry();
63 while (zipEntry != null)
64 {
65 entries.add(zipEntry);
66 zis.closeEntry();
67 zipEntry = zis.getNextEntry();
68 }
69 }
70 finally
71 {
72 IOUtils.closeQuietly(zis);
73 }
74
75 return (ZipEntry[]) entries.toArray(new ZipEntry[entries.size()]);
76 }
77
78 public void conditionalUnzip() throws IOException
79 {
80 Map<String,Long> zipContentsAndLastModified = new HashMap<String,Long>();
81
82 ZipEntry[] zipEntries = entries();
83 for (int i = 0; i < zipEntries.length; i++)
84 {
85 zipContentsAndLastModified.put(zipEntries[i].getName(), zipEntries[i].getTime());
86 }
87
88
89
90 Map<String,Long> targetDirContents = getContentsOfTargetDir(destDir);
91 if (!targetDirContents.equals(zipContentsAndLastModified))
92 {
93
94 if (destDir.exists())
95 {
96 FileUtils.cleanDirectory(destDir);
97 }
98 unzip();
99 }
100 else
101 {
102 log.debug("Target directory contents match zip contents. Do nothing.");
103 }
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 @VisibleForTesting
120 static String normaliseAndVerify(String name)
121 {
122 final String normalised = FilenameUtils.normalizeNoEndSeparator(name);
123 if (StringUtils.isBlank(normalised))
124 {
125 throw new IllegalArgumentException("Path name " + name + " is illegal");
126 }
127 return normalised;
128 }
129
130 private Map<String,Long> getContentsOfTargetDir(File dir)
131 {
132
133 final FilenameFilter filter = new FilenameFilter()
134 {
135 @Override
136 public boolean accept(File dir, String name)
137 {
138 return name.endsWith(".jar");
139 }
140 };
141
142
143 if (!dir.isDirectory())
144 {
145 return Collections.emptyMap();
146 }
147
148 Map<String,Long> targetDirContents = new HashMap<String,Long>();
149 for (File child : dir.listFiles())
150 {
151 if (log.isDebugEnabled())
152 {
153 log.debug("Examining entry in zip: "+child);
154 }
155 targetDirContents.put(child.getName(), child.lastModified());
156 }
157
158 return targetDirContents;
159 }
160 }