1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package io.earcam.instrumental.lade.jpms;
20
21 import static io.earcam.instrumental.archive.Archive.archive;
22 import static io.earcam.instrumental.archive.jpms.AsJpmsModule.asJpmsModule;
23 import static io.earcam.instrumental.archive.jpms.auto.ArchivePackageModuleMapper.fromArchives;
24 import static io.earcam.instrumental.lade.ClassLoaders.inMemoryClassLoader;
25 import static java.nio.charset.StandardCharsets.UTF_8;
26 import static java.util.stream.Collectors.toSet;
27 import static org.hamcrest.MatcherAssert.assertThat;
28 import static org.hamcrest.Matchers.contains;
29 import static org.hamcrest.Matchers.equalTo;
30 import static org.hamcrest.Matchers.is;
31
32 import java.io.ByteArrayOutputStream;
33 import java.io.IOException;
34 import java.io.PrintStream;
35 import java.lang.module.Configuration;
36 import java.lang.module.ModuleDescriptor;
37 import java.lang.module.ModuleFinder;
38 import java.lang.module.ModuleReader;
39 import java.lang.module.ModuleReference;
40 import java.lang.reflect.Method;
41 import java.util.NoSuchElementException;
42 import java.util.Set;
43
44 import org.junit.jupiter.api.Test;
45
46 import io.earcam.acme.api.AcmeApi;
47 import io.earcam.acme.app.AcmeApp;
48 import io.earcam.acme.imp.AcmeImp;
49 import io.earcam.instrumental.archive.Archive;
50 import io.earcam.instrumental.archive.AsJar;
51 import io.earcam.instrumental.lade.Handler;
52 import io.earcam.instrumental.lade.InMemoryClassLoader;
53 import io.earcam.instrumental.reflect.Methods;
54 import io.earcam.unexceptional.CheckedRunnable;
55 import io.earcam.unexceptional.Exceptional;
56
57 public class InMemoryModuleFinderTest {
58
59 @Test
60 void findsAllModules()
61 {
62 InMemoryClassLoader loader = inMemoryClassLoader()
63 .jar(archive()
64 .configured(
65 asJpmsModule()
66 .named("a")
67 .autoRequiring())
68 .toByteArray())
69 .jar(archive()
70 .configured(
71 AsJar.asJar()
72 .withManifestHeader("Name", "B"))
73 .toByteArray())
74 .jar(archive()
75 .configured(
76 asJpmsModule()
77 .named("c")
78 .autoRequiring())
79 .toByteArray());
80
81 InMemoryModuleFinder finder = new InMemoryModuleFinder(loader);
82
83 Set<String> names = finder.findAll().stream()
84 .map(ModuleReference::descriptor)
85 .map(ModuleDescriptor::name)
86 .collect(toSet());
87
88 assertThat(names, contains("a", "c"));
89 }
90
91
92 @Test
93 void wiresUpAndRuns() throws Exception
94 {
95
96
97
98 Archive api = archive()
99 .configured(
100 asJpmsModule()
101 .named("api")
102 .exporting(s -> true)
103 .autoRequiring()
104 )
105 .with(AcmeApi.class)
106 .toObjectModel();
107
108 Archive app = archive()
109 .configured(
110 asJpmsModule()
111 .named("app")
112 .autoRequiring()
113 .autoRequiring(fromArchives(api))
114 .exporting(s -> true)
115 .using(AcmeApi.class)
116 .launching(AcmeApp.class)
117 )
118 .toObjectModel();
119
120 Archive imp = archive()
121 .configured(
122 asJpmsModule()
123 .named("imp")
124 .autoRequiring()
125 .autoRequiring(fromArchives(api))
126 .providing(AcmeApi.class, AcmeImp.class)
127 ).toObjectModel();
128
129
130
131
132
133 Handler.addProtocolHandlerSystemProperty();
134
135 InMemoryClassLoader loader = inMemoryClassLoader()
136 .jar(api.toByteArray())
137 .jar(imp.toByteArray())
138 .jar(app.toByteArray());
139
140
141
142
143
144 ModuleFinder finder = new InMemoryModuleFinder(loader);
145 ModuleLayer parent = ModuleLayer.boot();
146
147 Configuration configuration = parent.configuration().resolveAndBind(
148 ModuleFinder.of(),
149 finder,
150 Set.of("app"));
151
152 ModuleLayer layer = parent.defineModulesWithOneLoader(configuration, loader);
153
154
155
156
157
158 Class<?> mainClass = layer.findLoader("app").loadClass(AcmeApp.class.getCanonicalName());
159 Method main = Methods.getMethod(mainClass, "main", String[].class)
160 .orElseThrow(NoSuchMethodError::new);
161
162 String you = "Dynamic & In-Mem";
163 String stdout = captureStdout(() -> main.invoke(null, new Object[] {new String[] {you}}));
164
165 String expectedResponse =
166 "module app: Acme Corp would like to say \"Hello\" to \"" +
167 you +
168 "\" (Terms and conditions apply)\n";
169
170 assertThat(stdout, is(equalTo(expectedResponse)));
171
172
173 }
174
175
176 private static String captureStdout(CheckedRunnable<ReflectiveOperationException> runnable)
177 {
178 PrintStream old = System.out;
179 try {
180 ByteArrayOutputStream baos = new ByteArrayOutputStream();
181 System.setOut(new PrintStream(baos));
182
183 Exceptional.run(runnable);
184 return new String(baos.toByteArray(), UTF_8);
185 } finally {
186 System.setOut(old);
187 }
188 }
189
190
191 @Test
192 void closeDoesNothing() throws IOException
193 {
194 InMemoryClassLoader loader = inMemoryClassLoader()
195 .jar(archive()
196 .configured(
197 asJpmsModule()
198 .named("a")
199 .autoRequiring())
200 .toByteArray());
201
202 InMemoryModuleFinder finder = new InMemoryModuleFinder(loader);
203
204 ModuleReader reader = finder.findAll().stream()
205 .map(Exceptional.uncheckFunction(ModuleReference::open))
206 .findFirst()
207 .orElseThrow(NoSuchElementException::new);
208
209 reader.close();
210
211 assertThat(reader.find("module-info.class").isPresent(), is(true));
212 }
213 }