51 * @modules jdk.jpackage/jdk.jpackage.internal |
51 * @modules jdk.jpackage/jdk.jpackage.internal |
52 * @compile MainClassTest.java |
52 * @compile MainClassTest.java |
53 * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main |
53 * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main |
54 * --jpt-run=jdk.jpackage.tests.MainClassTest |
54 * --jpt-run=jdk.jpackage.tests.MainClassTest |
55 * --jpt-space-subst=_ |
55 * --jpt-space-subst=_ |
56 * --jpt-exclude=modular=y;_main-class=n;_jar-main-class=b;_jlink=y |
|
57 */ |
56 */ |
58 |
57 |
59 public final class MainClassTest { |
58 public final class MainClassTest { |
60 |
59 |
61 static final class Script { |
60 static final class Script { |
62 Script() { |
61 Script() { |
63 appDesc = JavaAppDesc.parse("test.Hello"); |
62 appDesc = JavaAppDesc.parse("test.Hello"); |
64 } |
63 } |
65 |
64 |
66 Script modular(boolean v) { |
65 Script modular(boolean v) { |
67 appDesc.setModuleName(v ? null : "com.other"); |
66 appDesc.setModuleName(v ? "com.other" : null); |
68 return this; |
67 return this; |
69 } |
68 } |
70 |
69 |
71 Script withJLink(boolean v) { |
70 Script withJLink(boolean v) { |
72 withJLink = v; |
71 withJLink = v; |
136 |
135 |
137 nonExistingMainClass = Stream.of( |
136 nonExistingMainClass = Stream.of( |
138 script.appDesc.packageName(), "ThereIsNoSuchClass").filter( |
137 script.appDesc.packageName(), "ThereIsNoSuchClass").filter( |
139 Objects::nonNull).collect(Collectors.joining(".")); |
138 Objects::nonNull).collect(Collectors.joining(".")); |
140 |
139 |
141 cmd = JPackageCommand.helloAppImage(script.appDesc); |
140 cmd = JPackageCommand |
|
141 .helloAppImage(script.appDesc) |
|
142 .ignoreDefaultRuntime(true); |
142 if (!script.withJLink) { |
143 if (!script.withJLink) { |
143 cmd.setFakeRuntime(); |
144 cmd.addArguments("--runtime-image", Path.of(System.getProperty( |
|
145 "java.home"))); |
144 } |
146 } |
145 |
147 |
146 final String moduleName = script.appDesc.moduleName(); |
148 final String moduleName = script.appDesc.moduleName(); |
147 switch (script.mainClass) { |
149 switch (script.mainClass) { |
148 case NotSet: |
150 case NotSet: |
171 List<Script[]> scripts = new ArrayList<>(); |
173 List<Script[]> scripts = new ArrayList<>(); |
172 for (var withJLink : List.of(true, false)) { |
174 for (var withJLink : List.of(true, false)) { |
173 for (var modular : List.of(true, false)) { |
175 for (var modular : List.of(true, false)) { |
174 for (var mainClass : Script.MainClassType.values()) { |
176 for (var mainClass : Script.MainClassType.values()) { |
175 for (var jarMainClass : Script.MainClassType.values()) { |
177 for (var jarMainClass : Script.MainClassType.values()) { |
176 if (!withJLink && (jarMainClass == SetWrong || mainClass |
|
177 == SetWrong)) { |
|
178 // Without runtime can't run app to verify it will fail, so |
|
179 // there is no point in such testing. |
|
180 continue; |
|
181 } |
|
182 |
|
183 Script script = new Script() |
178 Script script = new Script() |
184 .modular(modular) |
179 .modular(modular) |
185 .withJLink(withJLink) |
180 .withJLink(withJLink) |
186 .withMainClass(mainClass) |
181 .withMainClass(mainClass) |
187 .withJarMainClass(jarMainClass); |
182 .withJarMainClass(jarMainClass); |
188 |
183 |
189 if (withMainClass.contains(jarMainClass) |
184 if (withMainClass.contains(jarMainClass) |
190 || withMainClass.contains(mainClass)) { |
185 || withMainClass.contains(mainClass)) { |
191 } else if (modular) { |
186 } else if (modular) { |
192 script.expectedErrorMessage( |
187 script.expectedErrorMessage( |
193 "A main class was not specified nor was one found in the jar"); |
188 "Error: Main application class is missing"); |
194 } else { |
189 } else { |
195 script.expectedErrorMessage( |
190 script.expectedErrorMessage( |
196 "Error: Main application class is missing"); |
191 "A main class was not specified nor was one found in the jar"); |
197 } |
192 } |
198 |
193 |
199 scripts.add(new Script[]{script}); |
194 scripts.add(new Script[]{script}); |
200 } |
195 } |
201 } |
196 } |
248 nonExistingMainClass)).apply(output.stream()); |
243 nonExistingMainClass)).apply(output.stream()); |
249 } |
244 } |
250 } |
245 } |
251 } |
246 } |
252 |
247 |
253 private void initJarWithWrongMainClass() { |
248 private void initJarWithWrongMainClass() throws IOException { |
254 // Call JPackageCommand.executePrerequisiteActions() to build app's jar. |
249 // Call JPackageCommand.executePrerequisiteActions() to build app's jar. |
255 // executePrerequisiteActions() is called by JPackageCommand instance |
250 // executePrerequisiteActions() is called by JPackageCommand instance |
256 // only once. |
251 // only once. |
257 cmd.executePrerequisiteActions(); |
252 cmd.executePrerequisiteActions(); |
258 |
253 |
259 Path jarFile; |
254 final Path jarFile; |
260 if (script.appDesc.moduleName() != null) { |
255 if (script.appDesc.moduleName() != null) { |
261 jarFile = Path.of(cmd.getArgumentValue("--module-path"), |
256 jarFile = Path.of(cmd.getArgumentValue("--module-path"), |
262 script.appDesc.jarFileName()); |
257 script.appDesc.jarFileName()); |
263 } else { |
258 } else { |
264 jarFile = cmd.inputDir().resolve(cmd.getArgumentValue("--main-jar")); |
259 jarFile = cmd.inputDir().resolve(cmd.getArgumentValue("--main-jar")); |
265 } |
260 } |
266 |
261 |
267 // Create jar file with the main class attribute in manifest set to |
262 // Create new jar file filtering out main class from the old jar file. |
268 // non-existing class. |
|
269 TKit.withTempDirectory("repack-jar", workDir -> { |
263 TKit.withTempDirectory("repack-jar", workDir -> { |
270 Path manifestFile = workDir.resolve("META-INF/MANIFEST.MF"); |
264 // Extract app's class from the old jar. |
271 try (var jar = new JarFile(jarFile.toFile())) { |
265 explodeJar(jarFile, workDir, |
272 jar.stream() |
266 jarEntry -> Path.of(jarEntry.getName()).equals( |
273 .filter(Predicate.not(JarEntry::isDirectory)) |
267 script.appDesc.classFilePath())); |
274 .sequential().forEachOrdered(ThrowingConsumer.toConsumer( |
268 |
275 jarEntry -> { |
269 // Create app's jar file with different main class. |
276 try (var in = jar.getInputStream(jarEntry)) { |
270 var badAppDesc = JavaAppDesc.parse(script.appDesc.toString()).setClassName( |
277 Path fileName = workDir.resolve(jarEntry.getName()); |
271 nonExistingMainClass); |
278 Files.createDirectories(fileName.getParent()); |
272 JPackageCommand.helloAppImage(badAppDesc).executePrerequisiteActions(); |
279 Files.copy(in, fileName); |
273 |
280 } |
274 // Extract new jar but skip app's class. |
281 })); |
275 explodeJar(jarFile, workDir, |
282 } |
276 jarEntry -> !Path.of(jarEntry.getName()).equals( |
|
277 badAppDesc.classFilePath())); |
|
278 |
|
279 // At this point we should have: |
|
280 // 1. Manifest from the new jar referencing non-existing class |
|
281 // as the main class. |
|
282 // 2. Module descriptor referencing non-existing class as the main |
|
283 // class in case of modular app. |
|
284 // 3. App's class from the old jar. We need it to let jlink find some |
|
285 // classes in the package declared in module descriptor |
|
286 // in case of modular app. |
283 |
287 |
284 Files.delete(jarFile); |
288 Files.delete(jarFile); |
285 |
|
286 // Adjust manifest. |
|
287 TKit.createTextFile(manifestFile, Files.readAllLines( |
|
288 manifestFile).stream().map(line -> line.replace( |
|
289 script.appDesc.className(), nonExistingMainClass))); |
|
290 |
|
291 new Executor().setToolProvider(JavaTool.JAR) |
289 new Executor().setToolProvider(JavaTool.JAR) |
292 .addArguments("-c", "-M", "-f", jarFile.toString()) |
290 .addArguments("-v", "-c", "-M", "-f", jarFile.toString()) |
293 .addArguments("-C", workDir.toString(), ".") |
291 .addArguments("-C", workDir.toString(), ".") |
|
292 .dumpOutput() |
294 .execute().assertExitCodeIsZero(); |
293 .execute().assertExitCodeIsZero(); |
295 }); |
294 }); |
|
295 } |
|
296 |
|
297 private static void explodeJar(Path jarFile, Path workDir, |
|
298 Predicate<JarEntry> filter) throws IOException { |
|
299 try (var jar = new JarFile(jarFile.toFile())) { |
|
300 jar.stream() |
|
301 .filter(Predicate.not(JarEntry::isDirectory)) |
|
302 .filter(filter) |
|
303 .sequential().forEachOrdered(ThrowingConsumer.toConsumer( |
|
304 jarEntry -> { |
|
305 try (var in = jar.getInputStream(jarEntry)) { |
|
306 Path fileName = workDir.resolve(jarEntry.getName()); |
|
307 Files.createDirectories(fileName.getParent()); |
|
308 Files.copy(in, fileName); |
|
309 } |
|
310 })); |
|
311 } |
296 } |
312 } |
297 |
313 |
298 private final JPackageCommand cmd; |
314 private final JPackageCommand cmd; |
299 private final Script script; |
315 private final Script script; |
300 private final String nonExistingMainClass; |
316 private final String nonExistingMainClass; |