1 /* 2 * Copyright (c) 2015, 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 java.io.IOException; 25 import java.io.OutputStream; 26 import java.io.File; 27 import java.nio.file.Files; 28 import java.nio.file.Path; 29 import java.nio.file.Paths; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.StringJoiner; 33 import java.util.Arrays; 34 import java.util.stream.Collectors; 35 import java.lang.module.ModuleDescriptor; 36 import jdk.testlibrary.OutputAnalyzer; 37 import org.testng.annotations.DataProvider; 38 import org.testng.annotations.Test; 39 import jdk.internal.module.ModuleInfoWriter; 40 import static java.lang.module.ModuleDescriptor.Builder; 41 42 /** 43 * Base class need to be extended by modular test for security. 44 */ 45 public abstract class ModularTest { 46 47 /** 48 * Enum represents all supported module types supported in JDK9. i.e. 49 * EXPLICIT - Modules have module descriptor(module-info.java) 50 * defining the module. 51 * AUTO - Are regular jar files but provided in MODULE_PATH instead 52 * of CLASS_PATH. 53 * UNNAMED - Are regular jar but provided through CLASS_PATH. 54 */ 55 public enum MODULE_TYPE { 56 57 EXPLICIT, AUTO, UNNAMED; 58 } 59 60 public static final String SPACE = " "; 61 public static final Path SRC = Paths.get(System.getProperty("test.src")); 62 public static final String DESCRIPTOR = "MetaService"; 63 public static final String MODULAR = "Modular"; 64 public static final String AUTO = "AutoServiceType"; 65 public static final String JAR_EXTN = ".jar"; 66 67 /** 68 * Setup test data for the test. 69 */ 70 @DataProvider(name = "TestParams") 71 public Object[][] setUpTestData() { 72 return getTestInput(); 73 } 74 75 /** 76 * Test method for TestNG. 77 */ 78 @Test(dataProvider = "TestParams") 79 public void runTest(MODULE_TYPE cModuleType, MODULE_TYPE sModuletype, 80 boolean addMetaDesc, String failureMsgExpected, String[] args) 81 throws Exception { 82 83 String testName = new StringJoiner("_").add(cModuleType.toString()) 84 .add(sModuletype.toString()).add( 85 (addMetaDesc) ? "WITH_SERVICE" : "NO_SERVICE") 86 .toString(); 87 88 System.out.format("%nStarting Test case: '%s'", testName); 89 Path cJarPath = findJarPath(false, cModuleType, false, 90 (sModuletype == MODULE_TYPE.EXPLICIT)); 91 Path sJarPath = findJarPath(true, sModuletype, addMetaDesc, false); 92 System.out.format("%nClient jar path : %s ", cJarPath); 93 System.out.format("%nService jar path : %s ", sJarPath); 94 OutputAnalyzer output = executeTestClient(cModuleType, cJarPath, 95 sModuletype, sJarPath, args); 96 97 if (output.getExitValue() != 0) { 98 if (failureMsgExpected != null 99 && output.getOutput().contains(failureMsgExpected)) { 100 System.out.println("PASS: Test is expected to fail here."); 101 } else { 102 System.out.format("%nUnexpected failure occured with exit code" 103 + " '%s'.", output.getExitValue()); 104 throw new RuntimeException("Unexpected failure occured."); 105 } 106 } 107 } 108 109 /** 110 * Abstract method need to be implemented by each Test type to provide 111 * test parameters. 112 */ 113 public abstract Object[][] getTestInput(); 114 115 /** 116 * Execute the test client to access required service. 117 */ 118 public abstract OutputAnalyzer executeTestClient(MODULE_TYPE cModuleType, 119 Path cJarPath, MODULE_TYPE sModuletype, Path sJarPath, 120 String... args) throws Exception; 121 122 /** 123 * Find the Jar for service/client based on module type and if service 124 * descriptor required. 125 */ 126 public abstract Path findJarPath(boolean service, MODULE_TYPE moduleType, 127 boolean addMetaDesc, boolean dependsOnServiceModule); 128 129 /** 130 * Constructs a Java Command line string based on modular structure followed 131 * by modular client and service. 132 */ 133 public String[] getJavaCommand(Path modulePath, String classPath, 134 String clientModuleName, String mainClass, 135 Map<String, String> vmArgs, String... options) throws IOException { 136 137 final StringJoiner command = new StringJoiner(SPACE, SPACE, SPACE); 138 vmArgs.forEach((key, value) -> command.add(key + value)); 139 if (modulePath != null) { 140 command.add("--module-path").add(modulePath.toFile().getCanonicalPath()); 141 } 142 if (classPath != null && classPath.length() > 0) { 143 command.add("-cp").add(classPath); 144 } 145 if (clientModuleName != null && clientModuleName.length() > 0) { 146 command.add("-m").add(clientModuleName + "/" + mainClass); 147 } else { 148 command.add(mainClass); 149 } 150 command.add(Arrays.stream(options).collect(Collectors.joining(SPACE))); 151 return command.toString().trim().split("[\\s]+"); 152 } 153 154 /** 155 * Generate ModuleDescriptor object for explicit/auto based client/Service 156 * modules type. 157 */ 158 public ModuleDescriptor generateModuleDescriptor(boolean isService, 159 MODULE_TYPE moduleType, String moduleName, String pkg, 160 String serviceInterface, String serviceImpl, 161 String serviceModuleName, List<String> requiredModules, 162 boolean depends) { 163 164 final Builder builder; 165 if (moduleType == MODULE_TYPE.EXPLICIT) { 166 System.out.format(" %nGenerating ModuleDescriptor object"); 167 builder = new Builder(moduleName).exports(pkg); 168 if (isService && serviceInterface != null && serviceImpl != null) { 169 builder.provides(serviceInterface, serviceImpl); 170 } else { 171 if (serviceInterface != null) { 172 builder.uses(serviceInterface); 173 } 174 if (depends) { 175 builder.requires(serviceModuleName); 176 } 177 } 178 } else { 179 System.out.format(" %nModuleDescriptor object not required."); 180 return null; 181 } 182 requiredModules.stream().forEach(reqMod -> builder.requires(reqMod)); 183 return builder.build(); 184 } 185 186 /** 187 * Generates service descriptor inside META-INF folder. 188 */ 189 public boolean createMetaInfServiceDescriptor( 190 Path serviceDescriptorFile, String serviceImpl) { 191 boolean created = true; 192 System.out.format("%nCreating META-INF service descriptor for '%s' " 193 + "at path '%s'", serviceImpl, serviceDescriptorFile); 194 try { 195 Path parent = serviceDescriptorFile.getParent(); 196 if (parent != null) { 197 Files.createDirectories(parent); 198 } 199 Files.write(serviceDescriptorFile, serviceImpl.getBytes("UTF-8")); 200 System.out.println( 201 "META-INF service descriptor generated successfully"); 202 } catch (IOException e) { 203 e.printStackTrace(System.out); 204 created = false; 205 } 206 return created; 207 } 208 209 /** 210 * Generate modular/regular jar file. 211 */ 212 public void generateJar(ModuleDescriptor mDescriptor, Path jar, 213 Path compilePath) throws IOException { 214 System.out.format("%nCreating jar file '%s'", jar); 215 JarUtils.createJarFile(jar, compilePath); 216 if (mDescriptor != null) { 217 Path dir = Files.createTempDirectory("tmp"); 218 Path mi = dir.resolve("module-info.class"); 219 try (OutputStream out = Files.newOutputStream(mi)) { 220 ModuleInfoWriter.write(mDescriptor, out); 221 } 222 System.out.format("%nAdding 'module-info.class' to jar '%s'", jar); 223 JarUtils.updateJarFile(jar, dir); 224 } 225 } 226 227 /** 228 * Copy pre-generated jar files to the module base path. 229 */ 230 public void copyJarsToModuleBase(MODULE_TYPE moduleType, Path jar, 231 Path mBasePath) throws IOException { 232 if (mBasePath != null) { 233 Files.createDirectories(mBasePath); 234 } 235 if (moduleType != MODULE_TYPE.UNNAMED) { 236 Path artifactName = mBasePath.resolve(jar.getFileName()); 237 System.out.format("%nCopy jar path: '%s' to module base path: %s", 238 jar, artifactName); 239 Files.copy(jar, artifactName); 240 } 241 } 242 243 /** 244 * Construct class path string. 245 */ 246 public String buildClassPath(MODULE_TYPE cModuleType, 247 Path cJarPath, MODULE_TYPE sModuletype, 248 Path sJarPath) throws IOException { 249 StringJoiner classPath = new StringJoiner(File.pathSeparator); 250 classPath.add((cModuleType == MODULE_TYPE.UNNAMED) 251 ? cJarPath.toFile().getCanonicalPath() : ""); 252 classPath.add((sModuletype == MODULE_TYPE.UNNAMED) 253 ? sJarPath.toFile().getCanonicalPath() : ""); 254 return classPath.toString(); 255 } 256 257 /** 258 * Construct executable module name for java. It is fixed for explicit 259 * module type while it is same as jar file name for automated module type. 260 */ 261 public String getModuleName(MODULE_TYPE moduleType, 262 Path jarPath, String mName) { 263 String jarName = jarPath.toFile().getName(); 264 return (moduleType == MODULE_TYPE.EXPLICIT) ? mName 265 : ((moduleType == MODULE_TYPE.AUTO) ? jarName.substring(0, 266 jarName.indexOf(JAR_EXTN)) : null); 267 } 268 269 /** 270 * Delete all the files inside the base module path. 271 */ 272 public void cleanModuleBasePath(Path mBasePath) { 273 Arrays.asList(mBasePath.toFile().listFiles()).forEach(f -> { 274 System.out.println("delete: " + f); 275 f.delete(); 276 }); 277 } 278 279 }