1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.codehaus.plexus.archiver.jar;
18
19 import java.io.File;
20 import java.io.InputStream;
21 import java.lang.reflect.Method;
22 import java.nio.file.attribute.FileTime;
23 import java.text.SimpleDateFormat;
24 import java.util.Arrays;
25 import java.util.Enumeration;
26 import java.util.HashSet;
27 import java.util.Set;
28 import java.util.zip.ZipEntry;
29 import java.util.zip.ZipFile;
30
31 import org.codehaus.plexus.archiver.ArchiverException;
32 import org.junit.jupiter.api.BeforeEach;
33 import org.junit.jupiter.api.Test;
34 import org.junit.jupiter.api.condition.DisabledIf;
35 import org.junit.jupiter.api.condition.EnabledIf;
36
37 import static org.junit.jupiter.api.Assertions.assertEquals;
38 import static org.junit.jupiter.api.Assertions.assertNotNull;
39 import static org.junit.jupiter.api.Assertions.assertThrows;
40
41 class JarToolModularJarArchiverTest extends BaseJarArchiverTest {
42
43 private ModularJarArchiver archiver;
44
45
46
47
48 @BeforeEach
49 void setup() throws Exception {
50 File jarFile = new File("target/output/modular.jar");
51 jarFile.delete();
52
53 archiver = getJarArchiver();
54 archiver.setDestFile(jarFile);
55 archiver.addDirectory(new File("src/test/resources/java-classes"));
56 }
57
58
59
60
61 @Test
62 @EnabledIf("modulesAreSupported")
63 void testModularJarWithMainClassAndVersion() throws Exception {
64 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
65 archiver.setModuleVersion("1.0.0");
66 archiver.setModuleMainClass("com.example.app.Main");
67
68 archiver.createArchive();
69
70
71 assertModularJarFile(
72 archiver.getDestFile(), "1.0.0", "com.example.app.Main", "com.example.app", "com.example.resources");
73 }
74
75
76
77
78
79
80 @Test
81 @EnabledIf("modulesAreSupported")
82 void testModularJarWithManifestAndModuleMainClass() throws Exception {
83 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
84 Manifest manifest = new Manifest();
85 manifest.addConfiguredAttribute(new Manifest.Attribute("Main-Class", "com.example.app.Main2"));
86 archiver.addConfiguredManifest(manifest);
87 archiver.setModuleMainClass("com.example.app.Main");
88
89 archiver.createArchive();
90
91
92
93 assertModularJarFile(
94 archiver.getDestFile(), null, "com.example.app.Main", "com.example.app", "com.example.resources");
95 assertManifestMainClass(archiver.getDestFile(), "com.example.app.Main");
96 }
97
98
99
100
101
102 @Test
103 @EnabledIf("modulesAreSupported")
104 void testModularJarWithManifestMainClassAttribute() throws Exception {
105 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
106 Manifest manifest = new Manifest();
107 manifest.addConfiguredAttribute(new Manifest.Attribute("Main-Class", "com.example.app.Main2"));
108 archiver.addConfiguredManifest(manifest);
109
110 archiver.createArchive();
111
112
113 assertModularJarFile(
114 archiver.getDestFile(), null, "com.example.app.Main2", "com.example.app", "com.example.resources");
115 assertManifestMainClass(archiver.getDestFile(), "com.example.app.Main2");
116 }
117
118
119
120
121 @Test
122 @EnabledIf("modulesAreSupported")
123 void testModularJar() throws Exception {
124 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
125 archiver.createArchive();
126
127
128 assertModularJarFile(archiver.getDestFile(), null, null, "com.example.app", "com.example.resources");
129 }
130
131
132
133
134 @Test
135 @EnabledIf("modulesAreSupported")
136 void testInvalidModularJar() throws Exception {
137 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
138
139 archiver.setModuleVersion("notAValidVersion");
140
141 assertThrows(ArchiverException.class, () -> archiver.createArchive());
142 }
143
144
145
146
147
148 @Test
149 @DisabledIf("modulesAreSupported")
150 void testModularJarPriorJava9() throws Exception {
151 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
152 archiver.setModuleVersion("1.0.0");
153 archiver.setModuleMainClass("com.example.app.Main");
154
155 archiver.createArchive();
156
157
158 try (ZipFile resultingArchive = new ZipFile(archiver.getDestFile())) {
159 assertNotNull(resultingArchive.getEntry("module-info.class"));
160 }
161 }
162
163
164
165
166 @Test
167 @EnabledIf("modulesAreSupported")
168 void testNoCompression() throws Exception {
169 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
170 archiver.setCompress(false);
171
172 archiver.createArchive();
173
174
175 try (ZipFile resultingArchive = new ZipFile(archiver.getDestFile())) {
176 Enumeration<? extends ZipEntry> entries = resultingArchive.entries();
177
178 while (entries.hasMoreElements()) {
179 ZipEntry entry = entries.nextElement();
180
181 assertEquals(ZipEntry.STORED, entry.getMethod());
182 }
183 }
184 }
185
186
187
188
189
190 @Test
191 @EnabledIf("modulesAreSupported")
192 void testCompression() throws Exception {
193 archiver.addDirectory(new File("src/test/resources/java-module-descriptor"));
194 archiver.addFile(new File("src/test/jars/test.jar"), "META-INF/lib/test.jar");
195 archiver.setRecompressAddedZips(false);
196
197 archiver.createArchive();
198
199
200 try (ZipFile resultingArchive = new ZipFile(archiver.getDestFile())) {
201 Enumeration<? extends ZipEntry> entries = resultingArchive.entries();
202
203 while (entries.hasMoreElements()) {
204 ZipEntry entry = entries.nextElement();
205
206 int expectedMethod =
207 entry.isDirectory() || entry.getName().endsWith(".jar") ? ZipEntry.STORED : ZipEntry.DEFLATED;
208 assertEquals(expectedMethod, entry.getMethod());
209 }
210 }
211 }
212
213
214
215
216 @Test
217 @EnabledIf("modulesAreSupported")
218 void testModularMultiReleaseJar() throws Exception {
219
220 archiver.addFile(
221 new File("src/test/resources/java-module-descriptor/module-info.class"),
222 "META-INF/versions/9/module-info.class");
223 archiver.addFile(new File("src/test/resources/java-module-descriptor/module-info.class"), "module-info.class");
224
225 Manifest manifest = new Manifest();
226 manifest.addConfiguredAttribute(new Manifest.Attribute("Main-Class", "com.example.app.Main2"));
227 manifest.addConfiguredAttribute(new Manifest.Attribute("Multi-Release", "true"));
228 archiver.addConfiguredManifest(manifest);
229
230 archiver.setModuleVersion("1.0.0");
231
232 archiver.setModuleMainClass("com.example.app.Main");
233
234 SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
235 long dateTimeMillis = isoFormat.parse("2020-02-29T23:59:59Z").getTime();
236 FileTime lastModTime = FileTime.fromMillis(dateTimeMillis);
237
238 archiver.configureReproducibleBuild(lastModTime);
239 archiver.createArchive();
240
241
242 long roundedDown = lastModTime.toMillis() - (lastModTime.toMillis() % 2000);
243
244 long expectedLastModifiedTime = normalizeLastModifiedTime(roundedDown);
245
246
247 try (ZipFile resultingArchive = new ZipFile(archiver.getDestFile())) {
248 ZipEntry moduleDescriptorEntry = resultingArchive.getEntry("META-INF/versions/9/module-info.class");
249 InputStream resultingModuleDescriptor = resultingArchive.getInputStream(moduleDescriptorEntry);
250 assertModuleDescriptor(
251 resultingModuleDescriptor,
252 "1.0.0",
253 "com.example.app.Main",
254 "com.example.app",
255 "com.example.resources");
256
257 ZipEntry rootModuleDescriptorEntry = resultingArchive.getEntry("module-info.class");
258 InputStream rootResultingModuleDescriptor = resultingArchive.getInputStream(rootModuleDescriptorEntry);
259 assertModuleDescriptor(
260 rootResultingModuleDescriptor,
261 "1.0.0",
262 "com.example.app.Main",
263 "com.example.app",
264 "com.example.resources");
265
266
267 Enumeration<? extends ZipEntry> entries = resultingArchive.entries();
268 while (entries.hasMoreElements()) {
269 ZipEntry element = entries.nextElement();
270 assertEquals(
271 expectedLastModifiedTime, element.getTime(), "Last Modified Time does not match with expected");
272 FileTime expectedFileTime = FileTime.fromMillis(expectedLastModifiedTime);
273 assertEquals(
274 expectedFileTime,
275 element.getLastModifiedTime(),
276 "Last Modified Time does not match with expected");
277 }
278 }
279 }
280
281 @Override
282 protected JarToolModularJarArchiver getJarArchiver() {
283 return new JarToolModularJarArchiver();
284 }
285
286 private void assertModularJarFile(
287 File jarFile, String expectedVersion, String expectedMainClass, String... expectedPackages)
288 throws Exception {
289 try (ZipFile resultingArchive = new ZipFile(jarFile)) {
290 ZipEntry moduleDescriptorEntry = resultingArchive.getEntry("module-info.class");
291 InputStream resultingModuleDescriptor = resultingArchive.getInputStream(moduleDescriptorEntry);
292
293 assertModuleDescriptor(resultingModuleDescriptor, expectedVersion, expectedMainClass, expectedPackages);
294 }
295 }
296
297 private void assertModuleDescriptor(
298 InputStream moduleDescriptorInputStream,
299 String expectedVersion,
300 String expectedMainClass,
301 String... expectedPackages)
302 throws Exception {
303
304 Class<?> moduleDescriptorClass = Class.forName("java.lang.module.ModuleDescriptor");
305 Class<?> optionalClass = Class.forName("java.util.Optional");
306 Method readMethod = moduleDescriptorClass.getMethod("read", InputStream.class);
307 Method mainClassMethod = moduleDescriptorClass.getMethod("mainClass");
308 Method rawVersionMethod = moduleDescriptorClass.getMethod("rawVersion");
309 Method packagesMethod = moduleDescriptorClass.getMethod("packages");
310 Method isPresentMethod = optionalClass.getMethod("isPresent");
311 Method getMethod = optionalClass.getMethod("get");
312
313
314 Object moduleDescriptor = readMethod.invoke(null, moduleDescriptorInputStream);
315
316
317 Object mainClassOptional = mainClassMethod.invoke(moduleDescriptor);
318 String actualMainClass = null;
319 if ((boolean) isPresentMethod.invoke(mainClassOptional)) {
320 actualMainClass = (String) getMethod.invoke(mainClassOptional);
321 }
322
323
324 Object versionOptional = rawVersionMethod.invoke(moduleDescriptor);
325 String actualVersion = null;
326 if ((boolean) isPresentMethod.invoke(versionOptional)) {
327 actualVersion = (String) getMethod.invoke(versionOptional);
328 }
329
330
331 Set<String> actualPackagesSet = (Set<String>) packagesMethod.invoke(moduleDescriptor);
332 Set<String> expectedPackagesSet = new HashSet<>(Arrays.asList(expectedPackages));
333
334 assertEquals(expectedMainClass, actualMainClass);
335 assertEquals(expectedVersion, actualVersion);
336 assertEquals(expectedPackagesSet, actualPackagesSet);
337 }
338
339 private void assertManifestMainClass(File jarFile, String expectedMainClass) throws Exception {
340 try (ZipFile resultingArchive = new ZipFile(jarFile)) {
341 ZipEntry manifestEntry = resultingArchive.getEntry("META-INF/MANIFEST.MF");
342 InputStream manifestInputStream = resultingArchive.getInputStream(manifestEntry);
343
344
345 Manifest manifest = new Manifest(manifestInputStream);
346 String actualManifestMainClass = manifest.getMainAttributes().getValue("Main-Class");
347
348 assertEquals(expectedMainClass, actualManifestMainClass);
349 }
350 }
351
352
353
354
355 private boolean modulesAreSupported() {
356 try {
357 Class.forName("java.lang.module.ModuleDescriptor");
358 } catch (ClassNotFoundException e) {
359 return false;
360 }
361
362 return true;
363 }
364 }