1 package com.atlassian.plugin.test;
2
3 import org.apache.commons.io.IOUtils;
4 import org.codehaus.commons.compiler.CompileException;
5 import org.codehaus.janino.ByteArrayClassLoader;
6 import org.codehaus.janino.Parser;
7 import org.codehaus.janino.SimpleCompiler;
8
9 import java.io.File;
10 import java.io.FileInputStream;
11 import java.io.FileOutputStream;
12 import java.io.IOException;
13 import java.io.StringReader;
14 import java.lang.reflect.Field;
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.zip.ZipEntry;
18 import java.util.zip.ZipOutputStream;
19
20
21
22
23 public class PluginJarBuilder {
24 private final Map<String, byte[]> jarContents;
25 private final String name;
26 private ClassLoader classLoader;
27
28
29
30
31 public PluginJarBuilder() {
32 this("test");
33 }
34
35
36
37
38
39
40 public PluginJarBuilder(String name) {
41 this(name, PluginJarBuilder.class.getClassLoader());
42 }
43
44
45
46
47
48
49 public PluginJarBuilder(String name, ClassLoader classLoader) {
50 jarContents = new HashMap<>();
51 this.name = name;
52 this.classLoader = classLoader;
53 }
54
55 public PluginJarBuilder manifest(Map<String, String> manifest) {
56 StringBuilder sb = new StringBuilder();
57 for (Map.Entry<String, String> entry : manifest.entrySet()) {
58 sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
59 }
60 sb.append('\n');
61 return addResource("META-INF/MANIFEST.MF", sb.toString());
62 }
63
64 public PluginJarBuilder addClass(Class<?> aClass) throws IOException {
65 jarContents.put(
66 aClass.getName().replace('.', '/') + ".class",
67 IOUtils.toByteArray(aClass.getResourceAsStream(aClass.getSimpleName() + ".class")));
68 return this;
69 }
70
71 public PluginJarBuilder addFormattedJava(String className, String... lines) throws Exception {
72 StringBuilder sb = new StringBuilder();
73 for (String line : lines) {
74 sb.append(line.replace('\'', '"')).append('\n');
75 }
76 return addJava(className, sb.toString());
77 }
78
79
80
81
82
83
84
85
86 public PluginJarBuilder addJava(String className, String code) throws Exception {
87 SimpleCompiler compiler = new SimpleCompiler();
88 compiler.setParentClassLoader(classLoader);
89 try {
90 compiler.cook(new StringReader(code));
91 } catch (CompileException ex) {
92 throw new IllegalArgumentException("Unable to compile " + className, ex);
93 }
94 classLoader = compiler.getClassLoader();
95
96
97
98 jarContents.put(className.replace('.', '/') + ".class", getClassFile(className));
99 return this;
100 }
101
102 public byte[] getClassFile(String className) throws NoSuchFieldException, IllegalAccessException {
103
104 ByteArrayClassLoader cl = (ByteArrayClassLoader) classLoader;
105 Field field = cl.getClass().getDeclaredField("classes");
106 field.setAccessible(true);
107 Map classes = (Map) field.get(cl);
108
109 return (byte[]) classes.get(className);
110 }
111
112
113
114
115
116
117
118 public PluginJarBuilder addResource(String path, String contents) {
119 jarContents.put(path, contents.getBytes());
120 return this;
121 }
122
123
124
125
126
127
128
129 public PluginJarBuilder addFormattedResource(String path, String... lines) {
130 StringBuilder sb = new StringBuilder();
131 for (String line : lines)
132 sb.append(line.replace('\'', '"')).append('\n');
133 jarContents.put(path, sb.toString().getBytes());
134 return this;
135 }
136
137 public PluginJarBuilder addPluginInformation(String key, String name, String version) {
138 return addPluginInformation(key, name, version, 2);
139 }
140
141 public PluginJarBuilder addPluginInformation(String key, String name, String version, int pluginsVersion) {
142 return addPluginInformation(key, name, version, pluginsVersion, null);
143 }
144
145 public PluginJarBuilder addPluginInformation(String key, String name, String version, int pluginsVersion, Map<String, String> params) {
146 StringBuilder sb = new StringBuilder();
147 sb.append("<atlassian-plugin name=\"").append(name).append("\" key=\"").append(key).append("\" pluginsVersion=\"").append(pluginsVersion).append("\">\n");
148 sb.append(" <plugin-info>\n");
149 sb.append(" <description>This plugin descriptor is used for testing plugins!</description>\n");
150 sb.append(" <version>").append(version).append("</version>\n");
151 sb.append(" <vendor name=\"Atlassian Software Systems Pty Ltd\" url=\"http://www.atlassian.com\" />\n");
152 if (params != null)
153 for (Map.Entry<String, String> param : params.entrySet())
154 sb.append("<param name=\"").append(param.getKey()).append("\">").append(param.getValue()).append("</param>\n");
155 sb.append(" </plugin-info>");
156 sb.append("</atlassian-plugin>");
157 jarContents.put("atlassian-plugin.xml", sb.toString().getBytes());
158 return this;
159 }
160
161
162
163
164
165
166
167 public PluginJarBuilder addFile(String path, File file) throws IOException {
168 FileInputStream in = new FileInputStream(file);
169 jarContents.put(path, IOUtils.toByteArray(in));
170 IOUtils.closeQuietly(in);
171 return this;
172 }
173
174
175
176
177
178
179
180 public File build() throws IOException {
181 return build(createBaseDir());
182 }
183
184 private File createBaseDir() {
185 File baseDir = new File("target");
186 if (!baseDir.exists())
187 baseDir = new File(System.getProperty("java.io.tmpdir"));
188 else {
189 baseDir = new File(baseDir, "tmp");
190 if (!baseDir.exists())
191 baseDir.mkdir();
192 }
193 return baseDir;
194 }
195
196 public File buildWithNoManifest() throws IOException {
197 return build(createBaseDir(), false);
198 }
199
200
201
202
203
204
205
206
207 public File build(File baseDir) throws IOException {
208 return build(baseDir, true);
209 }
210
211 private File build(File baseDir, boolean createManifest) throws IOException {
212
213 if (createManifest && !jarContents.containsKey("META-INF/MANIFEST.MF")) {
214 jarContents.put("META-INF/MANIFEST.MF", "Manifest-Version: 1.0".getBytes());
215 }
216
217 File jarFile = File.createTempFile(name, ".jar", baseDir);
218 try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(jarFile))) {
219 for (Map.Entry<String, byte[]> entry :jarContents.entrySet()) {
220 zout.putNextEntry(new ZipEntry(entry.getKey()));
221 zout.write(entry.getValue());
222 }
223 }
224 return jarFile;
225 }
226
227 public ClassLoader getClassLoader() {
228 return classLoader;
229 }
230
231 }