1 /* 2 * Copyright (c) 2019, 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 package jdk.jpackage.test; 24 25 import java.io.File; 26 import java.io.IOException; 27 import java.nio.file.Files; 28 import java.nio.file.Path; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.concurrent.atomic.AtomicBoolean; 32 import java.util.regex.Matcher; 33 import java.util.regex.Pattern; 34 import java.util.stream.Collectors; 35 import jdk.jpackage.test.Functional.ThrowingFunction; 36 import jdk.jpackage.test.Functional.ThrowingSupplier; 37 38 public class HelloApp { 39 40 HelloApp(JavaAppDesc appDesc) { 41 if (appDesc == null) { 42 this.appDesc = createDefaltAppDesc(); 43 } else { 44 this.appDesc = appDesc; 45 } 46 } 47 48 private JarBuilder prepareSources(Path srcDir) throws IOException { 49 final String qualifiedClassName = appDesc.className(); 50 51 final String className = qualifiedClassName.substring( 52 qualifiedClassName.lastIndexOf('.') + 1); 53 final String packageName = appDesc.packageName(); 54 55 final Path srcFile = srcDir.resolve(Path.of(String.join( 56 File.separator, qualifiedClassName.split("\\.")) + ".java")); 57 Files.createDirectories(srcFile.getParent()); 58 59 JarBuilder jarBuilder = createJarBuilder().addSourceFile(srcFile); 60 final String moduleName = appDesc.moduleName(); 61 if (moduleName != null) { 62 Path moduleInfoFile = srcDir.resolve("module-info.java"); 63 TKit.createTextFile(moduleInfoFile, List.of( 64 String.format("module %s {", moduleName), 65 String.format(" exports %s;", packageName), 66 " requires java.desktop;", 67 "}" 68 )); 69 jarBuilder.addSourceFile(moduleInfoFile); 70 jarBuilder.setModuleVersion(appDesc.moduleVersion()); 71 } 72 73 // Add package directive and replace class name in java source file. 74 // Works with simple test Hello.java. 75 // Don't expect too much from these regexps! 76 Pattern classNameRegex = Pattern.compile("\\bHello\\b"); 77 Pattern classDeclaration = Pattern.compile( 78 "(^.*\\bclass\\s+)\\bHello\\b(.*$)"); 79 Pattern importDirective = Pattern.compile( 80 "(?<=import (?:static )?+)[^;]+"); 81 AtomicBoolean classDeclared = new AtomicBoolean(); 82 AtomicBoolean packageInserted = new AtomicBoolean(packageName == null); 83 84 var packageInserter = Functional.identityFunction((line) -> { 85 packageInserted.setPlain(true); 86 return String.format("package %s;%s%s", packageName, 87 System.lineSeparator(), line); 88 }); 89 90 Files.write(srcFile, Files.readAllLines(HELLO_JAVA).stream().map(line -> { 91 Matcher m; 92 if (classDeclared.getPlain()) { 93 if ((m = classNameRegex.matcher(line)).find()) { 94 line = m.replaceAll(className); 95 } 96 return line; 97 } 98 99 if (!packageInserted.getPlain() && importDirective.matcher(line).find()) { 100 line = packageInserter.apply(line); 101 } else if ((m = classDeclaration.matcher(line)).find()) { 102 classDeclared.setPlain(true); 103 line = m.group(1) + className + m.group(2); 104 if (!packageInserted.getPlain()) { 105 line = packageInserter.apply(line); 106 } 107 } 108 return line; 109 }).collect(Collectors.toList())); 110 111 return jarBuilder; 112 } 113 114 private JarBuilder createJarBuilder() { 115 JarBuilder builder = new JarBuilder(); 116 if (appDesc.jarWithMainClass()) { 117 builder.setMainClass(appDesc.className()); 118 } 119 return builder; 120 } 121 122 void addTo(JPackageCommand cmd) { 123 final String moduleName = appDesc.moduleName(); 124 final String jarFileName = appDesc.jarFileName(); 125 final String qualifiedClassName = appDesc.className(); 126 127 if (moduleName != null && appDesc.packageName() == null) { 128 throw new IllegalArgumentException(String.format( 129 "Module [%s] with default package", moduleName)); 130 } 131 132 if (moduleName == null && CLASS_NAME.equals(qualifiedClassName)) { 133 // Use Hello.java as is. 134 cmd.addAction((self) -> { 135 Path jarFile = self.inputDir().resolve(jarFileName); 136 createJarBuilder().setOutputJar(jarFile).addSourceFile( 137 HELLO_JAVA).create(); 138 }); 139 } else { 140 cmd.addAction((self) -> { 141 final Path jarFile; 142 if (moduleName == null) { 143 jarFile = self.inputDir().resolve(jarFileName); 144 } else { 145 // `--module-path` option should be set by the moment 146 // when this action is being executed. 147 jarFile = Path.of(self.getArgumentValue("--module-path", 148 () -> self.inputDir().toString()), jarFileName); 149 Files.createDirectories(jarFile.getParent()); 150 } 151 152 TKit.withTempDirectory("src", 153 workDir -> prepareSources(workDir).setOutputJar(jarFile).create()); 154 }); 155 } 156 157 if (moduleName == null) { 158 cmd.addArguments("--main-jar", jarFileName); 159 cmd.addArguments("--main-class", qualifiedClassName); 160 } else { 161 cmd.addArguments("--module-path", TKit.workDir().resolve( 162 "input-modules")); 163 cmd.addArguments("--module", String.join("/", moduleName, 164 qualifiedClassName)); 165 // For modular app assume nothing will go in input directory and thus 166 // nobody will create input directory, so remove corresponding option 167 // from jpackage command line. 168 cmd.removeArgumentWithValue("--input"); 169 } 170 if (TKit.isWindows()) { 171 cmd.addArguments("--win-console"); 172 } 173 } 174 175 static JavaAppDesc createDefaltAppDesc() { 176 return new JavaAppDesc().setClassName(CLASS_NAME).setJarFileName( 177 "hello.jar"); 178 } 179 180 static void verifyOutputFile(Path outputFile, List<String> args) { 181 if (!outputFile.isAbsolute()) { 182 verifyOutputFile(outputFile.toAbsolutePath().normalize(), args); 183 return; 184 } 185 186 TKit.assertFileExists(outputFile); 187 188 List<String> contents = ThrowingSupplier.toSupplier( 189 () -> Files.readAllLines(outputFile)).get(); 190 191 List<String> expected = new ArrayList<>(List.of( 192 "jpackage test application", 193 String.format("args.length: %d", args.size()) 194 )); 195 expected.addAll(args); 196 197 TKit.assertStringListEquals(expected, contents, String.format( 198 "Check contents of [%s] file", outputFile)); 199 } 200 201 public static void executeLauncherAndVerifyOutput(JPackageCommand cmd) { 202 final Path launcherPath = cmd.appLauncherPath(); 203 if (!cmd.isFakeRuntime(String.format("Not running [%s] launcher", 204 launcherPath))) { 205 executeAndVerifyOutput(launcherPath, cmd.getAllArgumentValues( 206 "--arguments")); 207 } 208 } 209 210 public static void executeAndVerifyOutput(Path helloAppLauncher, 211 String... defaultLauncherArgs) { 212 executeAndVerifyOutput(helloAppLauncher, List.of(defaultLauncherArgs)); 213 } 214 215 public static void executeAndVerifyOutput(Path helloAppLauncher, 216 List<String> defaultLauncherArgs) { 217 // Output file will be created in the current directory. 218 Path outputFile = TKit.workDir().resolve(OUTPUT_FILENAME); 219 ThrowingFunction.toFunction(Files::deleteIfExists).apply(outputFile); 220 new Executor() 221 .setDirectory(outputFile.getParent()) 222 .setExecutable(helloAppLauncher) 223 .dumpOutput() 224 .execute() 225 .assertExitCodeIsZero(); 226 227 verifyOutputFile(outputFile, defaultLauncherArgs); 228 } 229 230 final static String OUTPUT_FILENAME = "appOutput.txt"; 231 232 private final JavaAppDesc appDesc; 233 234 private static final Path HELLO_JAVA = TKit.TEST_SRC_ROOT.resolve( 235 "apps/image/Hello.java"); 236 237 private final static String CLASS_NAME = HELLO_JAVA.getFileName().toString().split( 238 "\\.", 2)[0]; 239 }