1 /*
2  * Copyright (c) 2020, 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 import jdk.internal.access.JavaUtilJarAccess;
25 import jdk.internal.access.SharedSecrets;
26 
27 import java.io.*;
28 import java.nio.file.Path;
29 import java.util.*;
30 import java.util.concurrent.CountDownLatch;
31 import java.util.jar.JarFile;
32 
33 public class MultiThreadLoad {
34 
35     private static PrintStream out = System.err;
36     static String TEST_CLASS_PATH;
37     private static final JavaUtilJarAccess JUJA = SharedSecrets.javaUtilJarAccess();
38     private static CountDownLatch cdl = new CountDownLatch(1);
39 
40     private static <T> Set<T> setOf(Iterable<T> it) {
41         Set<T> s = new HashSet<T>();
42         for (T t : it)
43             s.add(t);
44         return s;
45     }
46 
47     private static <T> void checkEquals(Set<T> s1, Set<T> s2, boolean eq) {
48         if (s1.equals(s2) != eq)
49             throw new RuntimeException(String.format("%b %s : %s",
50                                                      eq, s1, s2));
51     }
52 
53     abstract static class TestLoader {
54         String name;
55 
56         TestLoader(String name) { this.name = name; }
57 
58         abstract ServiceLoader<FooService> load();
59     }
60 
61     static TestLoader tcclLoader = new TestLoader("Thread context class loader") {
62         ServiceLoader<FooService> load() {
63             return ServiceLoader.load(FooService.class);
64         }
65     };
66 
67     static TestLoader systemClLoader = new TestLoader("System class loader") {
68         ServiceLoader<FooService> load() {
69             return ServiceLoader.load(FooService.class, ClassLoader.getSystemClassLoader());
70         }
71     };
72 
main(String[] args)73     static TestLoader nullClLoader = new TestLoader("null (defer to system class loader)") {
74         ServiceLoader<FooService> load() {
75             return ServiceLoader.load(FooService.class, null);
76         }
77     };
78 
79     public static void main(String[] args) {
80         // keep reference to variables for the newly launced process
81         TEST_CLASS_PATH = args[0];
82         for (TestLoader tl : Arrays.asList(tcclLoader, systemClLoader, nullClLoader)) {
83             test(tl);
84         }
85     }
86 
87     static void test(TestLoader tl) {
88         Runnable r1 = () -> {
89             ServiceLoader<FooService> sl = tl.load();
90             out.format("%s: %s%n", tl.name, sl);
91 
92             // Providers are cached
93             Set<FooService> ps = setOf(sl);
94             cdl.countDown();
95             checkEquals(ps, setOf(sl), true);
96 
97             // The cache can be flushed and reloaded
98             sl.reload();
99             checkEquals(ps, setOf(sl), false);
initialize()100         };
101 
102         Runnable r2 = () -> {
103             jarCrawler(Path.of(TEST_CLASS_PATH));
104         };
105         new Thread(r2).start();
106         new Thread(r1).start();
107 
108     }
109 
110     private static void jarCrawler(Path p) {
111         try {
112             // let the other thread spin up
113             cdl.await();
114         } catch (InterruptedException e) {
115             // ignore
116         }
117         try {
118             for (int i = MultiProviderTest.NUM_JARS -1; i >= 0; i--) {
119                 JUJA.ensureInitialization(new JarFile(TEST_CLASS_PATH + File.separator
createProviderConfig(Path config, String providerName)120                         + "FooProvider" + i + ".jar"));
121             }
122         } catch (Exception e) {
123             System.out.println("Exception during jar crawl: ");
124             e.printStackTrace(System.out);
createJar(Path jar, String provider, List<String> files)125         }
126 
127     }
128 }
129