1 /*
2 * Copyright 2007-2008 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.lang.reflect.Method;
27 import java.module.Module;
28 import java.module.ModuleArchiveInfo;
29 import java.module.ModuleDefinition;
30 import java.module.Modules;
31 import java.module.Repository;
32 import java.module.RepositoryEvent;
33 import java.module.RepositoryListener;
34 import java.module.ModuleDefinition;
35 import java.module.VersionConstraint;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.HashMap;
39 import java.util.Map;
40 import sun.module.JamUtils;
41 import sun.module.repository.RepositoryUtils;
42
43 /**
44 * @test LocalRepositoryTest.java
45 * @summary Test LocalRepository.
46 * @library ../tools
47 * @compile -XDignore.symbol.file LocalRepositoryTest.java EventChecker.java ../tools/JamBuilder.java
48 * @run main LocalRepositoryTest
49 * */
50 public class LocalRepositoryTest {
51 static final boolean debug = System.getProperty("module.debug") != null;
52
53 static EventChecker ec = new EventChecker();
54
55 private static void println(String s) {
56 if (debug) System.err.println(s);
57 }
58
59 private static File makeTestDir(String name) throws IOException {
60 File rc =
61 new File(
62 System.getProperty("test.scratch", "."), name).getCanonicalFile();
63 if (rc.exists() ) {
64 JamUtils.recursiveDelete(rc);
65 }
66 rc.mkdirs();
67 return rc;
68 }
69
70 public static void realMain(String[] args) throws Throwable {
71
72 // Enables shadow file copies in the repository if we're running
73 // on Windows. This is to prevent file locking in the
74 // source location.
75 if (System.getProperty("os.platform").equalsIgnoreCase("windows")) {
76 System.setProperty("java.module.repository.shadowcopyfiles", "true");
77 }
78
79 File srcDir =
80 new File(
81 System.getProperty("test.scratch", "."), "LocalRepoSrc").getCanonicalFile();
82 if (srcDir.exists()) {
83 JamUtils.recursiveDelete(srcDir);
84 }
85 File expandDir = makeTestDir("LocalRepoExpand");
86
87 // Check that getApplicationRepository() doesn't return null and is
88 // configured OK.
89 Repository appRepo = Repository.getApplicationRepository();
90 check(appRepo != null);
91
92 Map<String, String> config = new HashMap<String, String>();
93 config.put("sun.module.repository.LocalRepository.cacheDirectory",
94 expandDir.getAbsolutePath());
95 Repository repo = Modules.newLocalRepository(
96 "test", srcDir, config, appRepo);
97
98 // Only REPOSITORY_INITIALIZED event should be fired.
99 check(ec.initializeEventExists(repo));
100 check(!ec.shutdownEventExists(repo));
101 check(!ec.installEventExists(repo, null));
102 check(!ec.uninstallEventExists(repo, null));
103
104 // In a different repository, verify we get an exception if we require
105 // the source dir to exist, but it does not.
106 Repository r2 = null;
107 try {
108 config.put("sun.module.repository.LocalRepository.sourceLocationMustExist", "true");
109 r2 = Modules.newLocalRepository(
110 "test", new File("doesNotExist"), config, appRepo);
111 fail();
112 } catch (IOException ex) {
113 check(ex.getMessage().contains("does not exist or is not a directory"));
114 } catch (Throwable t) {
115 unexpected(t);
116 }
117
118 // No event should be fired.
119 check(!ec.initializeEventExists(r2));
120 check(!ec.shutdownEventExists(r2));
121 check(!ec.installEventExists(r2, null));
122 check(!ec.uninstallEventExists(r2, null));
123
124 // Verify the repository is active and reloadable
125 check(repo.isActive());
126 check(repo.supportsReload());
127
128 // Verify the repository is read-only, since it's source location does
129 // not yet exist
130 check(repo.isReadOnly());
131
132 // Verify subsequent initialize is a no-op
133 try {
134 repo.initialize();
135 pass();
136 } catch (Throwable t) {
137 unexpected(t);
138 }
139
140 // No event should be fired.
141 check(!ec.initializeEventExists(repo));
142 check(!ec.shutdownEventExists(repo));
143 check(!ec.installEventExists(repo, null));
144 check(!ec.uninstallEventExists(repo, null));
145
146 // Check that find() and list() return nothing from repo, since the
147 // its source dir does not exist.
148 check(repo.list().size() == 0);
149 check(findModuleDefsInRepository(repo).size() == 0);
150
151 srcDir.mkdirs();
152 repo.reload();
153
154 // No event should be fired.
155 check(!ec.initializeEventExists(repo));
156 check(!ec.shutdownEventExists(repo));
157 check(!ec.installEventExists(repo, null));
158 check(!ec.uninstallEventExists(repo, null));
159
160 // Verify that repository is now *not* read-only
161 check(repo.isReadOnly() == false);
162
163 // Check that find() and list() return nothing from repo, since its
164 // source dir is empty.
165 check(repo.list().size() == 0);
166 check(findModuleDefsInRepository(repo).size() == 0);
167
168 File jamDir = makeTestDir("LocalRepoJam");
169
170 // Create a JAM file in the LocalRepository's sourceLocation
171 File jamFile = JamBuilder.createJam(
172 "localrepotest", "LocalRepoTestA", "LocalRepoModuleA", "3.1",
173 null, null, false, srcDir);
174
175 // Verify that we can reload from a read-only location
176 boolean readOnlyChangeOK = (srcDir.setWritable(false) == true);
177 repo.reload();
178
179 if (readOnlyChangeOK) {
180 // check(repo.isReadOnly());
181 }
182
183 // Check initial module is installed
184 List<ModuleArchiveInfo> installed = repo.list();
185 println("=installed size " + installed.size());
186 check(installed.size() == 1);
187 ModuleArchiveInfo mai = installed.get(0);
188 check(mai.getName().equals("LocalRepoModuleA"));
189
190 // MODULE_ARCHIVE_INSTALLED event should be fired.
191 check(!ec.initializeEventExists(repo));
192 check(!ec.shutdownEventExists(repo));
193 check(ec.installEventExists(repo, mai));
194 check(!ec.uninstallEventExists(repo, null));
195
196 // Verify module is runnable from read-only source location
197 runModule(repo, "LocalRepoModuleA");
198
199 // Verify that we can reload from a writable directory
200 readOnlyChangeOK = (srcDir.setWritable(true) == true);
201 repo.reload();
202 if (readOnlyChangeOK) {
203 check(repo.isReadOnly() == false);
204 }
205
206 // Check initial module is (still) installed
207 installed = repo.list();
208 println("=installed size " + installed.size());
209 check(installed.size() == 1);
210 check(installed.get(0).getName().equals("LocalRepoModuleA"));
211
212 // No event should be fired.
213 check(!ec.initializeEventExists(repo));
214 check(!ec.shutdownEventExists(repo));
215 check(!ec.installEventExists(repo, null));
216 check(!ec.uninstallEventExists(repo, null));
217
218 // Create platform-specific JAM
219 final String platform = RepositoryUtils.getPlatform();
220 final String arch = RepositoryUtils.getArch();
221
222 jamFile = JamBuilder.createJam(
223 "localrepotest", "LocalRepoTestB", "LocalRepoModuleB", "4.2",
224 platform, arch, true, jamDir);
225 mai = repo.install(jamFile.getCanonicalFile().toURI());
226 check(mai != null);
227 println("LocalRepoModuleB mai: " + mai);
228
229 // MODULE_ARCHIVE_INSTALLED event should be fired.
230 check(!ec.initializeEventExists(repo));
231 check(!ec.shutdownEventExists(repo));
232 check(ec.installEventExists(repo, mai));
233 check(!ec.uninstallEventExists(repo, null));
234
235 // Verify that same module cannot be over itself
236 try {
237 repo.install(jamFile.getCanonicalFile().toURI());
238 fail();
239 } catch (IllegalStateException ex) {
240 pass();
241 println("Caught expected " + ex);
242 }
243
244 // No event should be fired.
245 check(!ec.initializeEventExists(repo));
246 check(!ec.shutdownEventExists(repo));
247 check(!ec.installEventExists(repo, null));
248 check(!ec.uninstallEventExists(repo, null));
249
250 // Check that all modules are installed
251 installed = repo.list();
252 check(installed.size() == 2);
253 check(installed.get(0).getName().equals("LocalRepoModuleA"));
254 check(installed.get(1).getName().equals("LocalRepoModuleB"));
255
256 // Check that modules run
257 runModule(repo, "LocalRepoModuleA");
258 runModule(repo, "LocalRepoModuleB");
259
260 for (File f : srcDir.listFiles()) {
261 println("srcDir contains " + f.getName());
262 }
263
264 mai = installed.get(0);
265 repo.uninstall(mai);
266
267 // MODULE_ARCHIVE_UNINSTALLED should be fired.
268 check(!ec.initializeEventExists(repo));
269 check(!ec.shutdownEventExists(repo));
270 check(!ec.installEventExists(repo, null));
271 check(ec.uninstallEventExists(repo, mai));
272
273 installed = repo.list();
274 check(installed.size() == 1);
275 check(installed.get(0).getName().equals("LocalRepoModuleB"));
276
277 runModule(repo, "LocalRepoModuleB");
278
279 int numFound = repo.findAll().size();
280 int numInstalled = repo.list().size();
281
282 // See below where reload() is checked
283 ModuleArchiveInfo deleteMe = repo.list().get(0);
284
285 // Create another JAM for a different platform binding that
286 // does not match any in existence
287 jamFile = JamBuilder.createJam(
288 "localrepotest", "LocalRepoTestC", "LocalRepoModuleC", "2.7",
289 "abc" + platform, "def" + arch, false, jamDir);
290 mai = repo.install(jamFile.getCanonicalFile().toURI());
291 check(mai != null);
292 println("LocalRepoModuleC mai: " + mai);
293
294 // MODULE_ARCHIVE_INSTALLED event should be fired.
295 check(!ec.initializeEventExists(repo));
296 check(!ec.shutdownEventExists(repo));
297 check(ec.installEventExists(repo, mai));
298 check(!ec.uninstallEventExists(repo, null));
299
300 installed = repo.list();
301 check(installed.size() == 2);
302 mai = installed.get(1);
303 String name = mai.getName();
304 check(name.equals("LocalRepoModuleC"));
305 ModuleDefinition md = repo.find(name);
306 check(md == null);
307
308 // Check that one more module is listed, but the same number
309 // is findable.
310 check(repo.list().size() == numInstalled + 1);
311 check(repo.findAll().size() == numFound);
312
313 // Verify that if a JAM file is removed and the repository reloaded,
314 // the module corresponding to that JAM is not installed.
315 jamFile = new File(deleteMe.getFileName());
316 check(jamFile.isFile());
317 jamFile.delete();
318 repo.reload();
319 check(repo.find(mai.getName()) == null);
320
321 // MODULE_ARCHIVE_UNINSTALLED event should be fired.
322 check(!ec.initializeEventExists(repo));
323 check(!ec.shutdownEventExists(repo));
324 check(!ec.installEventExists(repo, null));
325 check(ec.uninstallEventExists(repo, deleteMe));
326
327 // Verify that updating a module works
328 JamBuilder jb = new JamBuilder(
329 "localrepotest", "LocalRepoTestD", "LocalRepoModuleD", "4.2",
330 platform, arch, false, jamDir);
331 jb.setMethod("foo");
332 jamFile = jb.createJam();
333 mai = repo.install(jamFile.getCanonicalFile().toURI());
334 runModule(repo, "LocalRepoModuleD", "foo");
335
336 // MODULE_ARCHIVE_INSTALLED event should be fired.
337 check(!ec.initializeEventExists(repo));
338 check(!ec.shutdownEventExists(repo));
339 check(ec.installEventExists(repo, mai));
340 check(!ec.uninstallEventExists(repo, null));
341
342 // Wait a bit before overwriting the just-created JAM
343 Thread.currentThread().sleep(1000);
344 jb = new JamBuilder(
345 "localrepotest", "LocalRepoTestD", "LocalRepoModuleD", "4.2",
346 platform, arch, false, srcDir); // Note: srcDir, not jamDir
347 jb.setMethod("bar");
348 jamFile = jb.createJam();
349 repo.reload();
350
351 // Both MODULE_ARCHIVE_UNINSTALLED and MODULE_ARCHIVE_INSTALLED events should be fired.
352 check(!ec.initializeEventExists(repo));
353 check(!ec.shutdownEventExists(repo));
354 check(ec.installEventExists(repo, null));
355 check(ec.uninstallEventExists(repo, mai));
356
357 runModule(repo, "LocalRepoModuleD", "bar");
358
359
360 // If there are three modules that have the same name and version but
361 // different platform binding:
362 // 1. Specific to xyz platform and arch
363 // 2. Specific to current platform and arch
364 // 3. Platform and arch neutral
365 //
366 // If they are installed in the order of #1, #2, #3, then #2 should
367 // be used to provide the module definition.
368 // Then, if they are uninstalled in the order of #1 and #2, then no
369 // module definition would be returned from the repository even
370 // #3 still exists, because #3 has not been loaded by the repository
371 // before.
372 //
373 // On the other hand, if they are installed in the order of #1, #3,
374 // #2, then #3 should be used to provide the module definition.
375 // Then, if they are uninstalled in the order of #3 and #1, then
376 // no module definition would be returned from the repository even
377 // #2 still exists, because #2 has not been loaded by the repository.
378 //
379 File jamFile1, jamFile2, jamFile3;
380 ModuleArchiveInfo mai1, mai2, mai3;
381 jamFile1 = JamBuilder.createJam(
382 "localrepotest", "LocalRepoTestE", "LocalRepoModuleE", "7.0",
383 "xyz-platform", "xyz-arch", false, jamDir);
384 jamFile2 = JamBuilder.createJam(
385 "localrepotest", "LocalRepoTestE", "LocalRepoModuleE", "7.0",
386 platform, arch, false, jamDir);
387 jamFile3 = JamBuilder.createJam(
388 "localrepotest", "LocalRepoTestE", "LocalRepoModuleE", "7.0",
389 null, null, false, jamDir);
390
391 // Installs #1, #2, and #3
392 mai1 = repo.install(jamFile1.getCanonicalFile().toURI());
393 check(mai1 != null);
394 mai2 = repo.install(jamFile2.getCanonicalFile().toURI());
395 check(mai2 != null);
396 mai3 = repo.install(jamFile3.getCanonicalFile().toURI());
397 check(mai3 != null);
398
399 md = repo.find("LocalRepoModuleE", VersionConstraint.valueOf("7.0"));
400 check(md != null);
401 java.module.annotation.PlatformBinding platformBinding = md.getAnnotation
402 (java.module.annotation.PlatformBinding.class);
403 if (platformBinding != null) {
404 if (platformBinding.platform().equals(platform)
405 && platformBinding.arch().equals(arch)) {
406 pass();
407 } else {
408 fail();
409 }
410 } else {
411 fail();
412 }
413
414 // Uninstall #1 and #2
415 check(repo.uninstall(mai1));
416 check(repo.uninstall(mai2));
417
418 md = repo.find("LocalRepoModuleE", VersionConstraint.valueOf("7.0"));
419 check (md == null);
420
421 repo.reload();
422 md = repo.find("LocalRepoModuleE", VersionConstraint.valueOf("7.0"));
423 check (md != null);
424
425 // Uninstall #3
426 check(repo.uninstall(mai3));
427
428 // Installs #1, #3, and #2
429 mai1 = repo.install(jamFile1.getCanonicalFile().toURI());
430 check(mai1 != null);
431 mai3 = repo.install(jamFile3.getCanonicalFile().toURI());
432 check(mai3 != null);
433 mai2 = repo.install(jamFile2.getCanonicalFile().toURI());
434 check(mai2 != null);
435
436 md = repo.find("LocalRepoModuleE", VersionConstraint.valueOf("7.0"));
437 check(md != null);
438 platformBinding = md.getAnnotation
439 (java.module.annotation.PlatformBinding.class);
440 if (platformBinding == null) {
441 pass();
442 } else {
443 fail();
444 }
445
446 // Uninstall #3 and #1
447 check(repo.uninstall(mai3));
448 check(repo.uninstall(mai1));
449
450 md = repo.find("LocalRepoModuleE", VersionConstraint.valueOf("7.0"));
451 check (md == null);
452
453 // Clear event queues for the tests afterwards
454 ec.clear();
455
456 repo.shutdown();
457
458 // REPOSITORY_SHUTDOWN event should be fired.
459 check(!ec.initializeEventExists(repo));
460 check(ec.shutdownEventExists(repo));
461 check(!ec.installEventExists(repo, null));
462 check(!ec.uninstallEventExists(repo, null));
463
464 // Verify cannot initialize() after shutdown()
465 try {
466 repo.initialize();
467 fail();
468 } catch (IllegalStateException ex) {
469 pass();
470 }
471
472 // No event should be fired.
473 check(!ec.initializeEventExists(repo));
474 check(!ec.shutdownEventExists(repo));
475 check(!ec.installEventExists(repo, null));
476 check(!ec.uninstallEventExists(repo, null));
477
478 // When not debugging and there are no failures, remove test dirs
479 if (!debug && failed == 0) {
480 JamUtils.recursiveDelete(srcDir);
481 JamUtils.recursiveDelete(jamDir);
482 JamUtils.recursiveDelete(expandDir);
483
484 repo.shutdown(); // sic: multiple shutdowns are OK
485
486 // No event should be fired.
487 check(!ec.initializeEventExists(repo));
488 check(!ec.shutdownEventExists(repo));
489 check(!ec.installEventExists(repo, null));
490 check(!ec.uninstallEventExists(repo, null));
491 }
492 }
493
494 static void runModule(Repository repo, String name) throws Exception {
495 runModule(repo, name, null);
496 }
497
498 static void runModule(Repository repo, String name, String methodName) throws Exception {
499 ModuleDefinition md = repo.find(name);
500 check(md != null);
501 println("=definition: " + md);
502
503 Module m = md.getModuleInstance();
504 String mainClass = md.getAnnotation(java.module.annotation.MainClass.class).value();
505 println("=mainclass: " + mainClass);
506 if (mainClass == null) {
507 throw new Exception("No Main-Class attribute in the module definition");
508 }
509
510 println("=module: " + m);
511 ClassLoader loader = m.getClassLoader();
512 println("=loader: " + loader);
513
514 Class<?> clazz = loader.loadClass(mainClass);
515 println("=class: " + clazz);
516
517 if (methodName == null) {
518 Method method = clazz.getMethod("main", String[].class);
519 method.invoke(null, (Object) (new String[0]));
520 } else {
521 Method method = clazz.getMethod(methodName, (Class<?>[]) null);
522 String rc = (String) method.invoke(null, new Object[0]);
523 check(methodName.equals(rc));
524 }
525 pass();
526 }
527
528 static List<ModuleDefinition> findModuleDefsInRepository(Repository r) {
529 List<ModuleDefinition> result = new ArrayList<ModuleDefinition>();
530 for (ModuleDefinition md : r.findAll()) {
531 if (md.getRepository() == r) {
532 result.add(md);
533 }
534 }
535 return result;
536 }
537
538 //--------------------- Infrastructure ---------------------------
539 static volatile int passed = 0, failed = 0;
540 static boolean pass() {passed++; return true;}
541 static boolean fail() {failed++; Thread.dumpStack(); return false;}
542 static boolean fail(String msg) {System.out.println(msg); return fail();}
543 static void unexpected(Throwable t) {failed++; t.printStackTrace();}
544 static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
545 static boolean equal(Object x, Object y) {
546 if (x == null ? y == null : x.equals(y)) return pass();
547 else return fail(x + " not equal to " + y);}
548 public static void main(String[] args) throws Throwable {
549 try {realMain(args);} catch (Throwable t) {unexpected(t);}
550 System.out.println("\nPassed = " + passed + " failed = " + failed);
551 if (failed > 0) throw new AssertionError("Some tests failed");}
552 }