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