1 /*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /**
25 * @test
26 * @library /lib/testlibrary
27 * @build AutomaticModulesTest ModuleUtils JarUtils
28 * @run testng AutomaticModulesTest
29 * @summary Basic tests for automatic modules
30 */
31
32 import java.io.IOException;
33 import java.lang.module.Configuration;
34 import java.lang.module.FindException;
35 import java.lang.module.ModuleDescriptor;
36 import java.lang.module.ModuleDescriptor.Exports;
37 import java.lang.module.ModuleDescriptor.Requires.Modifier;
38 import java.lang.module.ModuleFinder;
39 import java.lang.module.ModuleReference;
40 import java.lang.module.ResolvedModule;
41 import java.lang.reflect.Layer;
42 import java.lang.reflect.Module;
43 import java.nio.file.Files;
44 import java.nio.file.Path;
45 import java.nio.file.Paths;
46 import java.util.Optional;
47 import java.util.Set;
48 import java.util.jar.Attributes;
49 import java.util.jar.Manifest;
50 import java.util.stream.Collectors;
51 import java.util.stream.Stream;
52
53 import org.testng.annotations.DataProvider;
54 import org.testng.annotations.Test;
55 import static org.testng.Assert.*;
56
57 @Test
58 public class AutomaticModulesTest {
59
120 @Test(dataProvider = "names")
121 public void testNames(String fn, String mid) throws IOException {
122 String[] s = mid.split("/");
123 String mn = s[0];
124 String vs = (s.length == 2) ? s[1] : null;
125
126 Path dir = Files.createTempDirectory(USER_DIR, "mods");
127 Path jf = dir.resolve(fn);
128
129 // create empty JAR file
130 createDummyJarFile(jf);
131
132 // create a ModuleFinder to find modules in the directory
133 ModuleFinder finder = ModuleFinder.of(dir);
134
135 // a module with the expected name should be found
136 Optional<ModuleReference> mref = finder.find(mn);
137 assertTrue(mref.isPresent(), mn + " not found");
138
139 ModuleDescriptor descriptor = mref.get().descriptor();
140 assertEquals(descriptor.name(), mn);
141 if (vs == null) {
142 assertFalse(descriptor.version().isPresent());
143 } else {
144 assertEquals(descriptor.version().get().toString(), vs);
145 }
146 }
147
148
149 /**
150 * Test impossible mapping of JAR files to modules names
151 */
152 @Test(dataProvider = "badnames", expectedExceptions = FindException.class)
153 public void testBadNames(String fn, String ignore) throws IOException {
154 Path dir = Files.createTempDirectory(USER_DIR, "mods");
155 Path jf = dir.resolve(fn);
156
157 // create empty JAR file
158 createDummyJarFile(jf);
159
160 // should throw FindException
161 ModuleFinder.of(dir).findAll();
162 }
163
164
165 /**
166 * Test all packages are exported
167 */
168 public void testPackages() throws IOException {
169 Path dir = Files.createTempDirectory(USER_DIR, "mods");
170 createDummyJarFile(dir.resolve("m.jar"),
171 "p/C1.class", "p/C2.class", "q/C1.class");
172
173 ModuleFinder finder = ModuleFinder.of(dir);
174 Optional<ModuleReference> mref = finder.find("m");
175 assertTrue(mref.isPresent(), "m not found");
176
177 ModuleDescriptor descriptor = mref.get().descriptor();
178
179 assertTrue(descriptor.packages().size() == 2);
180 assertTrue(descriptor.packages().contains("p"));
181 assertTrue(descriptor.packages().contains("q"));
182
183 Set<String> exports = descriptor.exports().stream()
184 .map(Exports::source)
185 .collect(Collectors.toSet());
186 assertTrue(exports.size() == 2);
187 assertTrue(exports.contains("p"));
188 assertTrue(exports.contains("q"));
189 }
190
191 /**
192 * Test class files in JAR file where the entry does not correspond to a
193 * legal package name.
194 */
195 public void testBadPackage() throws IOException {
196 Path dir = Files.createTempDirectory(USER_DIR, "mods");
197 createDummyJarFile(dir.resolve("m.jar"), "p/C1.class", "p-/C2.class");
198
199 ModuleFinder finder = ModuleFinder.of(dir);
200 Optional<ModuleReference> mref = finder.find("m");
201 assertTrue(mref.isPresent(), "m not found");
202
203 ModuleDescriptor descriptor = mref.get().descriptor();
204
205 assertTrue(descriptor.packages().size() == 1);
206 assertTrue(descriptor.packages().contains("p"));
207
208 Set<String> exports = descriptor.exports().stream()
209 .map(Exports::source)
210 .collect(Collectors.toSet());
211 assertTrue(exports.size() == 1);
212 assertTrue(exports.contains("p"));
213 }
214
215 /**
216 * Test non-class resources in a JAR file.
217 */
218 public void testNonClassResources() throws IOException {
219 Path dir = Files.createTempDirectory(USER_DIR, "mods");
220 createDummyJarFile(dir.resolve("m.jar"),
221 "LICENSE",
222 "README",
223 "WEB-INF/tags",
224 "p/Type.class",
225 "p/resources/m.properties");
226
227 ModuleFinder finder = ModuleFinder.of(dir);
228 Optional<ModuleReference> mref = finder.find("m");
229 assertTrue(mref.isPresent(), "m not found");
230
231 ModuleDescriptor descriptor = mref.get().descriptor();
232
233 assertTrue(descriptor.packages().size() == 2);
234 assertTrue(descriptor.packages().contains("p"));
235 assertTrue(descriptor.packages().contains("p.resources"));
236 }
237
238 /**
239 * Test .class file in unnamed package (top-level directory)
240 */
241 @Test(expectedExceptions = FindException.class)
242 public void testClassInUnnamedPackage() throws IOException {
243 Path dir = Files.createTempDirectory(USER_DIR, "mods");
244 createDummyJarFile(dir.resolve("m.jar"), "Mojo.class");
245 ModuleFinder finder = ModuleFinder.of(dir);
246 finder.findAll();
247 }
248
249 /**
250 * Test JAR file with META-INF/services configuration file
251 */
252 public void testServicesConfiguration() throws IOException {
253 String service = "p.S";
254 String provider = "p.S1";
255
256 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
257 Path services = tmpdir.resolve("META-INF").resolve("services");
258 Files.createDirectories(services);
259 Files.write(services.resolve(service), Set.of(provider));
260 Path dir = Files.createTempDirectory(USER_DIR, "mods");
261 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
262
263 ModuleFinder finder = ModuleFinder.of(dir);
264
265 Optional<ModuleReference> mref = finder.find("m");
266 assertTrue(mref.isPresent(), "m not found");
267
268 ModuleDescriptor descriptor = mref.get().descriptor();
269 assertTrue(descriptor.provides().size() == 1);
270 ModuleDescriptor.Provides provides = descriptor.provides().iterator().next();
271 assertEquals(provides.service(), service);
272 assertTrue(provides.providers().size() == 1);
273 assertTrue(provides.providers().contains((provider)));
274 }
275
276
277 // META-INF/services files that don't map to legal service names
278 @DataProvider(name = "badservices")
279 public Object[][] createBadServices() {
297 Path services = tmpdir.resolve("META-INF").resolve("services");
298 Files.createDirectories(services);
299 Files.write(services.resolve(service), Set.of(provider));
300 Path dir = Files.createTempDirectory(USER_DIR, "mods");
301 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
302
303 Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m");
304 assertTrue(omref.isPresent());
305 ModuleDescriptor descriptor = omref.get().descriptor();
306 assertTrue(descriptor.provides().isEmpty());
307 }
308
309
310 // META-INF/services configuration file entries that are not legal
311 @DataProvider(name = "badproviders")
312 public Object[][] createBadProviders() {
313 return new Object[][] {
314
315 // service type provider type
316 { "p.S", "-" },
317 { "p.S", ".S1" },
318 { "p.S", "S1." },
319 };
320 }
321
322 /**
323 * Test JAR file with META-INF/services configuration file with bad
324 * values or names.
325 */
326 @Test(dataProvider = "badproviders", expectedExceptions = FindException.class)
327 public void testBadProvideNames(String service, String provider)
328 throws IOException
329 {
330 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
331 Path services = tmpdir.resolve("META-INF").resolve("services");
332 Files.createDirectories(services);
333 Files.write(services.resolve(service), Set.of(provider));
334 Path dir = Files.createTempDirectory(USER_DIR, "mods");
335 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
336
337 // should throw FindException
338 ModuleFinder.of(dir).findAll();
339 }
340
341
342 /**
343 * Test that a JAR file with a Main-Class attribute results
344 * in a module with a main class.
345 */
346 public void testMainClass() throws IOException {
347 String mainClass = "p.Main";
348
349 Manifest man = new Manifest();
350 Attributes attrs = man.getMainAttributes();
351 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
352 attrs.put(Attributes.Name.MAIN_CLASS, mainClass);
353
354 Path dir = Files.createTempDirectory(USER_DIR, "mods");
355 createDummyJarFile(dir.resolve("m.jar"), man);
356
357 ModuleFinder finder = ModuleFinder.of(dir);
358
359 Configuration parent = Layer.boot().configuration();
360 Configuration cf = resolve(parent, finder, "m");
361
362 ModuleDescriptor descriptor = findDescriptor(cf, "m");
363
364 assertTrue(descriptor.mainClass().isPresent());
365 assertEquals(descriptor.mainClass().get(), mainClass);
366 }
367
368
369 // Main-Class files that do not map to a legal Java identifier
370 @DataProvider(name = "badmainclass")
371 public Object[][] createBadMainClass() {
372 return new Object[][]{
373
374 { "p-.Main", null },
375 { ".Main", null }
376
377 };
378 }
379
380 /**
381 * Test that a JAR file with a Main-Class attribute that is not a valid
382 * Java identifier
383 */
384 @Test(dataProvider = "badmainclass", expectedExceptions = FindException.class)
385 public void testBadMainClass(String mainClass, String ignore) throws IOException {
386 Manifest man = new Manifest();
387 Attributes attrs = man.getMainAttributes();
388 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
389 attrs.put(Attributes.Name.MAIN_CLASS, mainClass);
390
391 Path dir = Files.createTempDirectory(USER_DIR, "mods");
392 createDummyJarFile(dir.resolve("m.jar"), man);
393
394 // should throw FindException
395 ModuleFinder.of(dir).findAll();
396 }
397
398
399 /**
400 * Basic test of a configuration created with automatic modules.
401 * a requires b*
402 * a requires c*
403 * b*
404 * c*
405 */
406 public void testConfiguration1() throws Exception {
407 ModuleDescriptor descriptor1
408 = ModuleDescriptor.module("a")
409 .requires("b")
410 .requires("c")
411 .requires("java.base")
412 .build();
413
414 // b and c are automatic modules
415 Path dir = Files.createTempDirectory(USER_DIR, "mods");
416 createDummyJarFile(dir.resolve("b.jar"), "p/T.class");
417 createDummyJarFile(dir.resolve("c.jar"), "q/T.class");
418
419 // module finder locates a and the modules in the directory
420 ModuleFinder finder
421 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1),
422 ModuleFinder.of(dir));
423
424 Configuration parent = Layer.boot().configuration();
425 Configuration cf = resolve(parent, finder, "a");
426
427 assertTrue(cf.modules().size() == 3);
428 assertTrue(cf.findModule("a").isPresent());
448
449 assertTrue(b.reads().contains(a));
450 assertTrue(b.reads().contains(c));
451 testReadAllBootModules(cf, "b"); // b reads all modules in boot layer
452
453 assertTrue(c.reads().contains(a));
454 assertTrue(c.reads().contains(b));
455 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer
456
457 }
458
459 /**
460 * Basic test of a configuration created with automatic modules
461 * a requires b
462 * b requires c*
463 * c*
464 * d*
465 */
466 public void testInConfiguration2() throws IOException {
467 ModuleDescriptor descriptor1
468 = ModuleDescriptor.module("a")
469 .requires("b")
470 .requires("java.base")
471 .build();
472
473 ModuleDescriptor descriptor2
474 = ModuleDescriptor.module("b")
475 .requires("c")
476 .requires("java.base")
477 .build();
478
479 // c and d are automatic modules
480 Path dir = Files.createTempDirectory(USER_DIR, "mods");
481 createDummyJarFile(dir.resolve("c.jar"), "p/T.class");
482 createDummyJarFile(dir.resolve("d.jar"), "q/T.class");
483
484 // module finder locates a and the modules in the directory
485 ModuleFinder finder
486 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2),
487 ModuleFinder.of(dir));
488
489 Configuration parent = Layer.boot().configuration();
490 Configuration cf = resolve(parent, finder, "a", "d");
491
492 assertTrue(cf.modules().size() == 4);
493 assertTrue(cf.findModule("a").isPresent());
494 assertTrue(cf.findModule("b").isPresent());
521 assertTrue(c.reads().contains(b));
522 assertTrue(c.reads().contains(d));
523 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer
524
525 assertTrue(d.reads().contains(a));
526 assertTrue(d.reads().contains(b));
527 assertTrue(d.reads().contains(c));
528 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer
529 }
530
531
532 /**
533 * Basic test of a configuration created with automatic modules
534 * a requires b
535 * b requires transitive c*
536 * c*
537 * d*
538 */
539 public void testInConfiguration3() throws IOException {
540 ModuleDescriptor descriptor1
541 = ModuleDescriptor.module("a")
542 .requires("b")
543 .requires("java.base")
544 .build();
545
546 ModuleDescriptor descriptor2
547 = ModuleDescriptor.module("b")
548 .requires(Set.of(Modifier.TRANSITIVE), "c")
549 .requires("java.base")
550 .build();
551
552 // c and d are automatic modules
553 Path dir = Files.createTempDirectory(USER_DIR, "mods");
554 createDummyJarFile(dir.resolve("c.jar"), "p/T.class");
555 createDummyJarFile(dir.resolve("d.jar"), "q/T.class");
556
557 // module finder locates a and the modules in the directory
558 ModuleFinder finder
559 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2),
560 ModuleFinder.of(dir));
561
562 Configuration parent = Layer.boot().configuration();
563 Configuration cf = resolve(parent, finder, "a", "d");
564
565 assertTrue(cf.modules().size() == 4);
566 assertTrue(cf.findModule("a").isPresent());
567 assertTrue(cf.findModule("b").isPresent());
592 assertTrue(b.reads().contains(d));
593 assertTrue(b.reads().contains(base));
594
595 assertTrue(reads(cf, "b", "c"));
596 assertTrue(reads(cf, "b", "d"));
597 assertTrue(reads(cf, "b", "java.base"));
598
599 assertTrue(c.reads().contains(a));
600 assertTrue(c.reads().contains(b));
601 assertTrue(c.reads().contains(d));
602 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer
603
604 assertTrue(d.reads().contains(a));
605 assertTrue(d.reads().contains(b));
606 assertTrue(d.reads().contains(c));
607 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer
608 }
609
610
611 /**
612 * Basic test of Layer containing automatic modules
613 */
614 public void testInLayer() throws IOException {
615 ModuleDescriptor descriptor
616 = ModuleDescriptor.module("a")
617 .requires("b")
618 .requires("c")
619 .build();
620
621 // b and c are simple JAR files
622 Path dir = Files.createTempDirectory(USER_DIR, "mods");
623 createDummyJarFile(dir.resolve("b.jar"), "p/T.class");
624 createDummyJarFile(dir.resolve("c.jar"), "q/T2.class");
625
626 // module finder locates a and the modules in the directory
627 ModuleFinder finder
628 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor),
629 ModuleFinder.of(dir));
630
631 Configuration parent = Layer.boot().configuration();
632 Configuration cf = resolve(parent, finder, "a");
633 assertTrue(cf.modules().size() == 3);
634
635 // each module gets its own loader
636 Layer layer = Layer.boot().defineModules(cf, mn -> new ClassLoader() { });
647 assertTrue(c.isNamed());
648 assertTrue(b.canRead(unnamed));
649 testsReadsAll(c, layer);
650 }
651
652
653 /**
654 * Test miscellaneous methods.
655 */
656 public void testMisc() throws IOException {
657 Path dir = Files.createTempDirectory(USER_DIR, "mods");
658 Path m_jar = createDummyJarFile(dir.resolve("m.jar"), "p/T.class");
659
660 ModuleFinder finder = ModuleFinder.of(m_jar);
661
662 assertTrue(finder.find("m").isPresent());
663 ModuleDescriptor m = finder.find("m").get().descriptor();
664
665 // test miscellaneous methods
666 assertTrue(m.isAutomatic());
667 assertFalse(m.isSynthetic());
668 assertFalse(m.osName().isPresent());
669 assertFalse(m.osArch().isPresent());
670 assertFalse(m.osVersion().isPresent());
671 }
672
673
674 /**
675 * Invokes parent.resolveRequires to resolve the given root modules.
676 */
677 static Configuration resolve(Configuration parent,
678 ModuleFinder finder,
679 String... roots) {
680 return parent.resolveRequires(finder, ModuleFinder.of(), Set.of(roots));
681 }
682
683 /**
684 * Finds a module in the given configuration or its parents, returning
685 * the module descriptor (or null if not found)
686 */
687 static ModuleDescriptor findDescriptor(Configuration cf, String name) {
688 Optional<ResolvedModule> om = cf.findModule(name);
689 if (om.isPresent()) {
690 return om.get().reference().descriptor();
691 } else {
692 return null;
693 }
694 }
695
696 /**
697 * Test that a module in a configuration reads all modules in the boot
698 * configuration.
699 */
700 static void testReadAllBootModules(Configuration cf, String mn) {
|
1 /*
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /**
25 * @test
26 * @library /lib/testlibrary
27 * @build AutomaticModulesTest ModuleUtils JarUtils
28 * @run testng AutomaticModulesTest
29 * @summary Basic tests for automatic modules
30 */
31
32 import java.io.IOException;
33 import java.lang.module.Configuration;
34 import java.lang.module.FindException;
35 import java.lang.module.ModuleDescriptor;
36 import java.lang.module.ModuleDescriptor.Requires.Modifier;
37 import java.lang.module.ModuleFinder;
38 import java.lang.module.ModuleReference;
39 import java.lang.module.ResolutionException;
40 import java.lang.module.ResolvedModule;
41 import java.lang.reflect.Layer;
42 import java.lang.reflect.Module;
43 import java.nio.file.Files;
44 import java.nio.file.Path;
45 import java.nio.file.Paths;
46 import java.util.Optional;
47 import java.util.Set;
48 import java.util.jar.Attributes;
49 import java.util.jar.Manifest;
50 import java.util.stream.Collectors;
51 import java.util.stream.Stream;
52
53 import org.testng.annotations.DataProvider;
54 import org.testng.annotations.Test;
55 import static org.testng.Assert.*;
56
57 @Test
58 public class AutomaticModulesTest {
59
120 @Test(dataProvider = "names")
121 public void testNames(String fn, String mid) throws IOException {
122 String[] s = mid.split("/");
123 String mn = s[0];
124 String vs = (s.length == 2) ? s[1] : null;
125
126 Path dir = Files.createTempDirectory(USER_DIR, "mods");
127 Path jf = dir.resolve(fn);
128
129 // create empty JAR file
130 createDummyJarFile(jf);
131
132 // create a ModuleFinder to find modules in the directory
133 ModuleFinder finder = ModuleFinder.of(dir);
134
135 // a module with the expected name should be found
136 Optional<ModuleReference> mref = finder.find(mn);
137 assertTrue(mref.isPresent(), mn + " not found");
138
139 ModuleDescriptor descriptor = mref.get().descriptor();
140 assertTrue(descriptor.isAutomatic());
141 assertEquals(descriptor.name(), mn);
142 if (vs == null) {
143 assertFalse(descriptor.version().isPresent());
144 } else {
145 assertEquals(descriptor.version().get().toString(), vs);
146 }
147 }
148
149
150 /**
151 * Test impossible mapping of JAR files to modules names
152 */
153 @Test(dataProvider = "badnames", expectedExceptions = FindException.class)
154 public void testBadNames(String fn, String ignore) throws IOException {
155 Path dir = Files.createTempDirectory(USER_DIR, "mods");
156 Path jf = dir.resolve(fn);
157
158 // create empty JAR file
159 createDummyJarFile(jf);
160
161 // should throw FindException
162 ModuleFinder.of(dir).findAll();
163 }
164
165
166 /**
167 * Test all packages are exported
168 */
169 public void testPackages() throws IOException {
170 Path dir = Files.createTempDirectory(USER_DIR, "mods");
171 createDummyJarFile(dir.resolve("m.jar"),
172 "p/C1.class", "p/C2.class", "q/C1.class");
173
174 ModuleFinder finder = ModuleFinder.of(dir);
175 Optional<ModuleReference> mref = finder.find("m");
176 assertTrue(mref.isPresent(), "m not found");
177
178 ModuleDescriptor descriptor = mref.get().descriptor();
179 assertTrue(descriptor.isAutomatic());
180
181 assertTrue(descriptor.packages().size() == 2);
182 assertTrue(descriptor.packages().contains("p"));
183 assertTrue(descriptor.packages().contains("q"));
184
185 assertTrue(descriptor.exports().isEmpty());
186 assertTrue(descriptor.opens().isEmpty());
187 }
188
189 /**
190 * Test class files in JAR file where the entry does not correspond to a
191 * legal package name.
192 */
193 public void testBadPackage() throws IOException {
194 Path dir = Files.createTempDirectory(USER_DIR, "mods");
195 createDummyJarFile(dir.resolve("m.jar"), "p/C1.class", "p-/C2.class");
196
197 ModuleFinder finder = ModuleFinder.of(dir);
198 Optional<ModuleReference> mref = finder.find("m");
199 assertTrue(mref.isPresent(), "m not found");
200
201 ModuleDescriptor descriptor = mref.get().descriptor();
202 assertTrue(descriptor.isAutomatic());
203
204 assertTrue(descriptor.packages().size() == 1);
205 assertTrue(descriptor.packages().contains("p"));
206
207 assertTrue(descriptor.exports().isEmpty());
208 assertTrue(descriptor.opens().isEmpty());
209 }
210
211 /**
212 * Test non-class resources in a JAR file.
213 */
214 public void testNonClassResources() throws IOException {
215 Path dir = Files.createTempDirectory(USER_DIR, "mods");
216 createDummyJarFile(dir.resolve("m.jar"),
217 "LICENSE",
218 "README",
219 "WEB-INF/tags",
220 "p/Type.class",
221 "p/resources/m.properties");
222
223 ModuleFinder finder = ModuleFinder.of(dir);
224 Optional<ModuleReference> mref = finder.find("m");
225 assertTrue(mref.isPresent(), "m not found");
226
227 ModuleDescriptor descriptor = mref.get().descriptor();
228 assertTrue(descriptor.isAutomatic());
229
230 assertTrue(descriptor.packages().size() == 1);
231 assertTrue(descriptor.packages().contains("p"));
232 }
233
234 /**
235 * Test .class file in unnamed package (top-level directory)
236 */
237 @Test(expectedExceptions = FindException.class)
238 public void testClassInUnnamedPackage() throws IOException {
239 Path dir = Files.createTempDirectory(USER_DIR, "mods");
240 createDummyJarFile(dir.resolve("m.jar"), "Mojo.class");
241 ModuleFinder finder = ModuleFinder.of(dir);
242 finder.findAll();
243 }
244
245 /**
246 * Test JAR file with META-INF/services configuration file
247 */
248 public void testServicesConfiguration() throws IOException {
249 String service = "p.S";
250 String provider = "p.S1";
251
252 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
253
254 // provider class
255 Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class");
256 Files.createDirectories(providerClass.getParent());
257 Files.createFile(providerClass);
258
259 // services configuration file
260 Path services = tmpdir.resolve("META-INF").resolve("services");
261 Files.createDirectories(services);
262 Files.write(services.resolve(service), Set.of(provider));
263
264 Path dir = Files.createTempDirectory(USER_DIR, "mods");
265 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
266
267 ModuleFinder finder = ModuleFinder.of(dir);
268
269 Optional<ModuleReference> mref = finder.find("m");
270 assertTrue(mref.isPresent(), "m not found");
271
272 ModuleDescriptor descriptor = mref.get().descriptor();
273 assertTrue(descriptor.provides().size() == 1);
274 ModuleDescriptor.Provides provides = descriptor.provides().iterator().next();
275 assertEquals(provides.service(), service);
276 assertTrue(provides.providers().size() == 1);
277 assertTrue(provides.providers().contains((provider)));
278 }
279
280
281 // META-INF/services files that don't map to legal service names
282 @DataProvider(name = "badservices")
283 public Object[][] createBadServices() {
301 Path services = tmpdir.resolve("META-INF").resolve("services");
302 Files.createDirectories(services);
303 Files.write(services.resolve(service), Set.of(provider));
304 Path dir = Files.createTempDirectory(USER_DIR, "mods");
305 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
306
307 Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m");
308 assertTrue(omref.isPresent());
309 ModuleDescriptor descriptor = omref.get().descriptor();
310 assertTrue(descriptor.provides().isEmpty());
311 }
312
313
314 // META-INF/services configuration file entries that are not legal
315 @DataProvider(name = "badproviders")
316 public Object[][] createBadProviders() {
317 return new Object[][] {
318
319 // service type provider type
320 { "p.S", "-" },
321 { "p.S", "p..S1" },
322 { "p.S", "S1." },
323 };
324 }
325
326 /**
327 * Test JAR file with META-INF/services configuration file with bad
328 * values or names.
329 */
330 @Test(dataProvider = "badproviders", expectedExceptions = FindException.class)
331 public void testBadProviderNames(String service, String provider)
332 throws IOException
333 {
334 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
335
336 // provider class
337 Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class");
338 Files.createDirectories(providerClass.getParent());
339 Files.createFile(providerClass);
340
341 // services configuration file
342 Path services = tmpdir.resolve("META-INF").resolve("services");
343 Files.createDirectories(services);
344 Files.write(services.resolve(service), Set.of(provider));
345
346 Path dir = Files.createTempDirectory(USER_DIR, "mods");
347 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
348
349 // should throw FindException
350 ModuleFinder.of(dir).findAll();
351 }
352
353 /**
354 * Test JAR file with META-INF/services configuration file listing a
355 * provider that is not in the module.
356 */
357 @Test(expectedExceptions = FindException.class)
358 public void testMissingProviderPackage() throws IOException {
359 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
360
361 // services configuration file
362 Path services = tmpdir.resolve("META-INF").resolve("services");
363 Files.createDirectories(services);
364 Files.write(services.resolve("p.S"), Set.of("q.P"));
365
366 Path dir = Files.createTempDirectory(USER_DIR, "mods");
367 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
368
369 // should throw FindException
370 ModuleFinder.of(dir).findAll();
371 }
372
373
374 /**
375 * Test that a JAR file with a Main-Class attribute results
376 * in a module with a main class.
377 */
378 public void testMainClass() throws IOException {
379 String mainClass = "p.Main";
380
381 Manifest man = new Manifest();
382 Attributes attrs = man.getMainAttributes();
383 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
384 attrs.put(Attributes.Name.MAIN_CLASS, mainClass);
385
386 Path dir = Files.createTempDirectory(USER_DIR, "mods");
387 String entry = mainClass.replace('.', '/') + ".class";
388 createDummyJarFile(dir.resolve("m.jar"), man, entry);
389
390 ModuleFinder finder = ModuleFinder.of(dir);
391
392 Configuration parent = Layer.boot().configuration();
393 Configuration cf = resolve(parent, finder, "m");
394
395 ModuleDescriptor descriptor = findDescriptor(cf, "m");
396
397 assertTrue(descriptor.mainClass().isPresent());
398 assertEquals(descriptor.mainClass().get(), mainClass);
399 }
400
401
402 // Main-Class files that do not map to a legal qualified type name
403 @DataProvider(name = "badmainclass")
404 public Object[][] createBadMainClass() {
405 return new Object[][]{
406
407 { "Main", null },
408 { "p..Main", null },
409 { "p-.Main", null },
410
411 };
412 }
413
414 /**
415 * Test that a JAR file with a Main-Class attribute that is not a qualified
416 * type name.
417 */
418 @Test(dataProvider = "badmainclass", expectedExceptions = FindException.class)
419 public void testBadMainClass(String mainClass, String ignore) throws IOException {
420 Manifest man = new Manifest();
421 Attributes attrs = man.getMainAttributes();
422 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
423 attrs.put(Attributes.Name.MAIN_CLASS, mainClass);
424
425 Path dir = Files.createTempDirectory(USER_DIR, "mods");
426 String entry = mainClass.replace('.', '/') + ".class";
427 createDummyJarFile(dir.resolve("m.jar"), man, entry);
428
429 // should throw FindException
430 ModuleFinder.of(dir).findAll();
431 }
432
433 /**
434 * Test that a JAR file with a Main-Class attribute that is not in the module
435 */
436 @Test(expectedExceptions = FindException.class)
437 public void testMissingMainClassPackage() throws IOException {
438 Manifest man = new Manifest();
439 Attributes attrs = man.getMainAttributes();
440 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
441 attrs.put(Attributes.Name.MAIN_CLASS, "p.Main");
442
443 Path dir = Files.createTempDirectory(USER_DIR, "mods");
444 createDummyJarFile(dir.resolve("m.jar"), man);
445
446 // should throw FindException
447 ModuleFinder.of(dir).findAll();
448 }
449
450
451 /**
452 * Basic test of a configuration created with automatic modules.
453 * a requires b*
454 * a requires c*
455 * b*
456 * c*
457 */
458 public void testConfiguration1() throws Exception {
459 ModuleDescriptor descriptor1
460 = ModuleDescriptor.newModule("a")
461 .requires("b")
462 .requires("c")
463 .requires("java.base")
464 .build();
465
466 // b and c are automatic modules
467 Path dir = Files.createTempDirectory(USER_DIR, "mods");
468 createDummyJarFile(dir.resolve("b.jar"), "p/T.class");
469 createDummyJarFile(dir.resolve("c.jar"), "q/T.class");
470
471 // module finder locates a and the modules in the directory
472 ModuleFinder finder
473 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1),
474 ModuleFinder.of(dir));
475
476 Configuration parent = Layer.boot().configuration();
477 Configuration cf = resolve(parent, finder, "a");
478
479 assertTrue(cf.modules().size() == 3);
480 assertTrue(cf.findModule("a").isPresent());
500
501 assertTrue(b.reads().contains(a));
502 assertTrue(b.reads().contains(c));
503 testReadAllBootModules(cf, "b"); // b reads all modules in boot layer
504
505 assertTrue(c.reads().contains(a));
506 assertTrue(c.reads().contains(b));
507 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer
508
509 }
510
511 /**
512 * Basic test of a configuration created with automatic modules
513 * a requires b
514 * b requires c*
515 * c*
516 * d*
517 */
518 public void testInConfiguration2() throws IOException {
519 ModuleDescriptor descriptor1
520 = ModuleDescriptor.newModule("a")
521 .requires("b")
522 .requires("java.base")
523 .build();
524
525 ModuleDescriptor descriptor2
526 = ModuleDescriptor.newModule("b")
527 .requires("c")
528 .requires("java.base")
529 .build();
530
531 // c and d are automatic modules
532 Path dir = Files.createTempDirectory(USER_DIR, "mods");
533 createDummyJarFile(dir.resolve("c.jar"), "p/T.class");
534 createDummyJarFile(dir.resolve("d.jar"), "q/T.class");
535
536 // module finder locates a and the modules in the directory
537 ModuleFinder finder
538 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2),
539 ModuleFinder.of(dir));
540
541 Configuration parent = Layer.boot().configuration();
542 Configuration cf = resolve(parent, finder, "a", "d");
543
544 assertTrue(cf.modules().size() == 4);
545 assertTrue(cf.findModule("a").isPresent());
546 assertTrue(cf.findModule("b").isPresent());
573 assertTrue(c.reads().contains(b));
574 assertTrue(c.reads().contains(d));
575 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer
576
577 assertTrue(d.reads().contains(a));
578 assertTrue(d.reads().contains(b));
579 assertTrue(d.reads().contains(c));
580 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer
581 }
582
583
584 /**
585 * Basic test of a configuration created with automatic modules
586 * a requires b
587 * b requires transitive c*
588 * c*
589 * d*
590 */
591 public void testInConfiguration3() throws IOException {
592 ModuleDescriptor descriptor1
593 = ModuleDescriptor.newModule("a")
594 .requires("b")
595 .requires("java.base")
596 .build();
597
598 ModuleDescriptor descriptor2
599 = ModuleDescriptor.newModule("b")
600 .requires(Set.of(Modifier.TRANSITIVE), "c")
601 .requires("java.base")
602 .build();
603
604 // c and d are automatic modules
605 Path dir = Files.createTempDirectory(USER_DIR, "mods");
606 createDummyJarFile(dir.resolve("c.jar"), "p/T.class");
607 createDummyJarFile(dir.resolve("d.jar"), "q/T.class");
608
609 // module finder locates a and the modules in the directory
610 ModuleFinder finder
611 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2),
612 ModuleFinder.of(dir));
613
614 Configuration parent = Layer.boot().configuration();
615 Configuration cf = resolve(parent, finder, "a", "d");
616
617 assertTrue(cf.modules().size() == 4);
618 assertTrue(cf.findModule("a").isPresent());
619 assertTrue(cf.findModule("b").isPresent());
644 assertTrue(b.reads().contains(d));
645 assertTrue(b.reads().contains(base));
646
647 assertTrue(reads(cf, "b", "c"));
648 assertTrue(reads(cf, "b", "d"));
649 assertTrue(reads(cf, "b", "java.base"));
650
651 assertTrue(c.reads().contains(a));
652 assertTrue(c.reads().contains(b));
653 assertTrue(c.reads().contains(d));
654 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer
655
656 assertTrue(d.reads().contains(a));
657 assertTrue(d.reads().contains(b));
658 assertTrue(d.reads().contains(c));
659 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer
660 }
661
662
663 /**
664 * Basic test of a configuration created with automatic modules
665 * a requires b* and c*
666 * b* contains p
667 * c* contains p
668 */
669 @Test(expectedExceptions = { ResolutionException.class })
670 public void testDuplicateSuppliers1() throws IOException {
671 ModuleDescriptor descriptor
672 = ModuleDescriptor.newModule("a")
673 .requires("b")
674 .requires("c")
675 .build();
676
677 // c and d are automatic modules with the same package
678 Path dir = Files.createTempDirectory(USER_DIR, "mods");
679 createDummyJarFile(dir.resolve("b.jar"), "p/T.class");
680 createDummyJarFile(dir.resolve("c.jar"), "p/T.class");
681
682 // module finder locates 'a' and the modules in the directory
683 ModuleFinder finder
684 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor),
685 ModuleFinder.of(dir));
686
687 Configuration parent = Layer.boot().configuration();
688 resolve(parent, finder, "a");
689 }
690
691
692 /**
693 * Basic test of a configuration created with automatic modules
694 * a contains p, requires b*
695 * b* contains p
696 */
697 @Test(expectedExceptions = { ResolutionException.class })
698 public void testDuplicateSuppliers2() throws IOException {
699 ModuleDescriptor descriptor
700 = ModuleDescriptor.newModule("a")
701 .packages(Set.of("p"))
702 .requires("b")
703 .build();
704
705 // c and d are automatic modules with the same package
706 Path dir = Files.createTempDirectory(USER_DIR, "mods");
707 createDummyJarFile(dir.resolve("b.jar"), "p/T.class");
708
709 // module finder locates 'a' and the modules in the directory
710 ModuleFinder finder
711 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor),
712 ModuleFinder.of(dir));
713
714 Configuration parent = Layer.boot().configuration();
715 resolve(parent, finder, "a");
716 }
717
718
719 /**
720 * Basic test of Layer containing automatic modules
721 */
722 public void testInLayer() throws IOException {
723 ModuleDescriptor descriptor
724 = ModuleDescriptor.newModule("a")
725 .requires("b")
726 .requires("c")
727 .build();
728
729 // b and c are simple JAR files
730 Path dir = Files.createTempDirectory(USER_DIR, "mods");
731 createDummyJarFile(dir.resolve("b.jar"), "p/T.class");
732 createDummyJarFile(dir.resolve("c.jar"), "q/T2.class");
733
734 // module finder locates a and the modules in the directory
735 ModuleFinder finder
736 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor),
737 ModuleFinder.of(dir));
738
739 Configuration parent = Layer.boot().configuration();
740 Configuration cf = resolve(parent, finder, "a");
741 assertTrue(cf.modules().size() == 3);
742
743 // each module gets its own loader
744 Layer layer = Layer.boot().defineModules(cf, mn -> new ClassLoader() { });
755 assertTrue(c.isNamed());
756 assertTrue(b.canRead(unnamed));
757 testsReadsAll(c, layer);
758 }
759
760
761 /**
762 * Test miscellaneous methods.
763 */
764 public void testMisc() throws IOException {
765 Path dir = Files.createTempDirectory(USER_DIR, "mods");
766 Path m_jar = createDummyJarFile(dir.resolve("m.jar"), "p/T.class");
767
768 ModuleFinder finder = ModuleFinder.of(m_jar);
769
770 assertTrue(finder.find("m").isPresent());
771 ModuleDescriptor m = finder.find("m").get().descriptor();
772
773 // test miscellaneous methods
774 assertTrue(m.isAutomatic());
775 assertFalse(m.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC));
776 assertFalse(m.osName().isPresent());
777 assertFalse(m.osArch().isPresent());
778 assertFalse(m.osVersion().isPresent());
779 }
780
781
782 /**
783 * Invokes parent.resolve to resolve the given root modules.
784 */
785 static Configuration resolve(Configuration parent,
786 ModuleFinder finder,
787 String... roots) {
788 return parent.resolve(finder, ModuleFinder.of(), Set.of(roots));
789 }
790
791 /**
792 * Finds a module in the given configuration or its parents, returning
793 * the module descriptor (or null if not found)
794 */
795 static ModuleDescriptor findDescriptor(Configuration cf, String name) {
796 Optional<ResolvedModule> om = cf.findModule(name);
797 if (om.isPresent()) {
798 return om.get().reference().descriptor();
799 } else {
800 return null;
801 }
802 }
803
804 /**
805 * Test that a module in a configuration reads all modules in the boot
806 * configuration.
807 */
808 static void testReadAllBootModules(Configuration cf, String mn) {
|