1 package com.atlassian.plugin;
2
3 import com.google.common.base.Predicate;
4 import org.apache.commons.io.IOUtils;
5 import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory;
7
8 import java.io.BufferedInputStream;
9 import java.io.File;
10 import java.io.FileInputStream;
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.util.jar.JarEntry;
15 import java.util.jar.JarFile;
16 import java.util.jar.Manifest;
17 import java.util.zip.ZipEntry;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.collect.Iterators.any;
21 import static com.google.common.collect.Iterators.forEnumeration;
22
23
24
25
26
27
28
29 public final class JarPluginArtifact implements PluginArtifact, PluginArtifact.AllowsReference
30 {
31 private final Logger logger = LoggerFactory.getLogger(this.getClass());
32
33 private final File jarFile;
34
35
36
37
38 final ReferenceMode referenceMode;
39
40
41
42
43
44 public JarPluginArtifact(File jarFile)
45 {
46 this(jarFile, ReferenceMode.FORBID_REFERENCE);
47 }
48
49
50
51
52
53
54 public JarPluginArtifact(File jarFile, ReferenceMode referenceMode)
55 {
56 this.jarFile = checkNotNull(jarFile);
57 this.referenceMode = referenceMode;
58 }
59
60
61 public boolean doesResourceExist(String name)
62 {
63 InputStream in = null;
64 try
65 {
66 in = getResourceAsStream(name);
67 return (in != null);
68 }
69 finally
70 {
71 IOUtils.closeQuietly(in);
72 }
73 }
74
75
76
77
78 public InputStream getResourceAsStream(String fileName) throws PluginParseException
79 {
80 checkNotNull(fileName, "The file name must not be null");
81
82 final JarFile jar = open();
83 final ZipEntry entry = jar.getEntry(fileName);
84 if (entry == null)
85 {
86 closeJarQuietly(jar);
87 return null;
88 }
89
90 try
91 {
92 return new BufferedInputStream(jar.getInputStream(entry))
93 {
94
95
96 public void close() throws IOException
97 {
98 super.close();
99 jar.close();
100 }
101 };
102 }
103 catch (IOException e)
104 {
105 throw new PluginParseException("Cannot retrieve " + fileName + " from plugin JAR [" + jarFile + "]", e);
106 }
107 }
108
109 public String getName()
110 {
111 return jarFile.getName();
112 }
113
114 @Override
115 public String toString()
116 {
117 return getName();
118 }
119
120
121
122
123
124 public InputStream getInputStream()
125 {
126 try
127 {
128 return new BufferedInputStream(new FileInputStream(jarFile));
129 }
130 catch (FileNotFoundException e)
131 {
132 throw new PluginParseException("Could not open JAR file: " + jarFile, e);
133 }
134 }
135
136 public File toFile()
137 {
138 return jarFile;
139 }
140
141 @Override
142 public boolean containsJavaExecutableCode()
143 {
144 final JarFile jar = open();
145 try
146 {
147 final Manifest manifest = getManifest(jar);
148 return hasBundleActivator(manifest)
149 ||
150 hasSpringContext(manifest)
151 ||
152 any(forEnumeration(jar.entries()), new Predicate<JarEntry>()
153 {
154 @Override
155 public boolean apply(JarEntry entry)
156 {
157 return isJavaClass(entry) || isJavaLibrary(entry) || isSpringContext(entry);
158 }
159 });
160 }
161 finally
162 {
163 closeJarQuietly(jar);
164 }
165 }
166
167 @Override
168 public boolean allowsReference()
169 {
170 return referenceMode.allowsReference();
171 }
172
173 private boolean isJavaClass(ZipEntry entry)
174 {
175 return entry.getName().endsWith(".class");
176 }
177
178 private boolean isJavaLibrary(ZipEntry entry)
179 {
180 return entry.getName().endsWith(".jar");
181 }
182
183 private boolean isSpringContext(ZipEntry entry)
184 {
185 final String entryName = entry.getName();
186 return entryName.startsWith("META-INF/spring/") && entryName.endsWith(".xml");
187 }
188
189 private boolean hasSpringContext(Manifest manifest)
190 {
191 return hasManifestEntry(manifest, "Spring-Context");
192 }
193
194 private boolean hasBundleActivator(Manifest manifest)
195 {
196 return hasManifestEntry(manifest, "Bundle-Activator");
197 }
198
199 private boolean hasManifestEntry(Manifest manifest, String manifestEntryName)
200 {
201 return manifest != null
202 && manifest.getMainAttributes() != null
203 && manifest.getMainAttributes().getValue(manifestEntryName) != null;
204 }
205
206 private JarFile open()
207 {
208 try
209 {
210 return new JarFile(jarFile);
211 }
212 catch (IOException e)
213 {
214 throw new PluginParseException("Cannot open JAR file: " + jarFile, e);
215 }
216 }
217
218 private Manifest getManifest(JarFile jar)
219 {
220 try
221 {
222 return jar.getManifest();
223 }
224 catch (IOException e)
225 {
226 throw new PluginParseException("Cannot get manifest for JAR file: " + jarFile, e);
227 }
228 }
229
230 private void closeJarQuietly(JarFile jar)
231 {
232 if (jar != null)
233 {
234 try
235 {
236 jar.close();
237 }
238 catch (IOException ignored)
239 {
240 logger.debug("Exception closing jar file " + jarFile + ".", ignored);
241 }
242 }
243 }
244 }