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.nio.file.Files; 26 import java.nio.file.Path; 27 import java.util.List; 28 import java.util.Map; 29 import java.lang.module.ModuleDescriptor; 30 import static java.lang.module.ModuleDescriptor.Builder; 31 32 /** 33 * Jigsaw utility methods are part of this class. It exposes methods to generate 34 * module descriptor object and create service descriptor inside META-INF folder 35 * etc. 36 */ 37 public class JigsawSecurityUtils { 38 39 /** 40 * Enum represents all supported module types in JDK9. 41 */ 42 public enum MODULE_TYPE { 43 44 EXPLICIT, AUTO, UNNAMED; 45 } 46 47 public static final String SPACE = " "; 48 49 /** 50 * Constructs a Java Command line string based on modular structure followed 51 * by modular client and service. 52 */ 53 public static String[] getJavaCommand(Path modulePath, 54 StringBuilder classPath, String clientModuleName, 55 String mainClass, Map<String, String> vmArgs) throws IOException { 56 57 final StringBuilder command = new StringBuilder(); 58 vmArgs.forEach((key, value) -> command.append(key + value + SPACE)); 59 if (modulePath != null) { 60 command.append("-mp" + SPACE + modulePath.toRealPath() + SPACE); 61 } 62 if (classPath != null && classPath.length() > 0) { 63 command.append("-cp" + SPACE + classPath + SPACE); 64 } 65 if (clientModuleName != null && clientModuleName.length() > 0) { 66 command.append("-m" + SPACE + clientModuleName + "/"); 67 } 68 command.append(mainClass); 69 return command.toString().trim().split("[\\s]+"); 70 } 71 72 /** 73 * Generate ModuleDescriptor object for explicit/auto based client/service 74 * modules. 75 */ 76 public static ModuleDescriptor generateModuleDescriptor( 77 boolean service, MODULE_TYPE moduleType, String moduleName, 78 String pkg, String serviceInterface, 79 String serviceImpl, String serviceModuleName, 80 List<String> requiredModules, boolean depends) { 81 final Builder builder; 82 if (moduleType == MODULE_TYPE.EXPLICIT) { 83 System.out.println("Generating ModuleDescriptor object"); 84 builder = new Builder(moduleName).exports(pkg); 85 if (service) { 86 builder.provides(serviceInterface, serviceImpl); 87 } else { 88 builder.uses(serviceInterface); 89 if (depends) { 90 builder.requires(serviceModuleName); 91 } 92 } 93 } else { 94 System.out.println("ModuleDescriptor object not required."); 95 return null; 96 } 97 requiredModules.stream().forEach(reqMod -> builder.requires(reqMod)); 98 99 return builder.build(); 100 } 101 102 /** 103 * Generates service descriptor inside META-INF folder. 104 */ 105 public static boolean createMetaInfServiceDescriptor( 106 Path serviceDescriptorFile, String serviceImpl) { 107 boolean created = true; 108 System.out.println(String.format("Creating META-INF service descriptor" 109 + " for '%s' at path '%s'", serviceImpl, 110 serviceDescriptorFile)); 111 try { 112 Path parent = serviceDescriptorFile.getParent(); 113 if (parent != null) { 114 Files.createDirectories(parent); 115 } 116 Files.write(serviceDescriptorFile, serviceImpl.getBytes("UTF-8")); 117 System.out.println(String.format( 118 "META-INF service descriptor generated successfully")); 119 } catch (IOException e) { 120 e.printStackTrace(System.out); 121 created = false; 122 } 123 return created; 124 } 125 126 } | 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) ? "DESCRIPTOR" : "NO_DESCRIPTOR") 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("-mp").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) { 169 builder.provides(serviceInterface, serviceImpl); 170 } else { 171 builder.uses(serviceInterface); 172 if (depends) { 173 builder.requires(serviceModuleName); 174 } 175 } 176 } else { 177 System.out.format(" %nModuleDescriptor object not required."); 178 return null; 179 } 180 requiredModules.stream().forEach(reqMod -> builder.requires(reqMod)); 181 return builder.build(); 182 } 183 184 /** 185 * Generates service descriptor inside META-INF folder. 186 */ 187 public boolean createMetaInfServiceDescriptor( 188 Path serviceDescriptorFile, String serviceImpl) { 189 boolean created = true; 190 System.out.format("%nCreating META-INF service descriptor for '%s' " 191 + "at path '%s'", serviceImpl, serviceDescriptorFile); 192 try { 193 Path parent = serviceDescriptorFile.getParent(); 194 if (parent != null) { 195 Files.createDirectories(parent); 196 } 197 Files.write(serviceDescriptorFile, serviceImpl.getBytes("UTF-8")); 198 System.out.println( 199 "META-INF service descriptor generated successfully"); 200 } catch (IOException e) { 201 e.printStackTrace(System.out); 202 created = false; 203 } 204 return created; 205 } 206 207 /** 208 * Generate modular/regular jar file. 209 */ 210 public void generateJar(ModuleDescriptor mDescriptor, Path jar, 211 Path compilePath) throws IOException { 212 System.out.format("%nCreating jar file '%s'", jar); 213 JarUtils.createJarFile(jar, compilePath); 214 if (mDescriptor != null) { 215 Path dir = Files.createTempDirectory("tmp"); 216 Path mi = dir.resolve("module-info.class"); 217 try (OutputStream out = Files.newOutputStream(mi)) { 218 ModuleInfoWriter.write(mDescriptor, out); 219 } 220 System.out.format("%nAdding 'module-info.class' to jar '%s'", jar); 221 JarUtils.updateJarFile(jar, dir); 222 } 223 } 224 225 /** 226 * Copy pre-generated jar files to the module base path. 227 */ 228 public void copyJarsToModuleBase(MODULE_TYPE moduleType, Path jar, 229 Path mBasePath) throws IOException { 230 if (mBasePath != null) { 231 Files.createDirectories(mBasePath); 232 } 233 if (moduleType != MODULE_TYPE.UNNAMED) { 234 Path artifactName = mBasePath.resolve(jar.getFileName()); 235 System.out.format("%nCopy jar path: '%s' to module base path: %s", 236 jar, artifactName); 237 Files.copy(jar, artifactName); 238 } 239 } 240 241 /** 242 * Construct class path string. 243 */ 244 public String buildClassPath(MODULE_TYPE cModuleType, 245 Path cJarPath, MODULE_TYPE sModuletype, 246 Path sJarPath) throws IOException { 247 StringJoiner classPath = new StringJoiner(File.pathSeparator); 248 classPath.add((cModuleType == MODULE_TYPE.UNNAMED) 249 ? cJarPath.toFile().getCanonicalPath() : ""); 250 classPath.add((sModuletype == MODULE_TYPE.UNNAMED) 251 ? sJarPath.toFile().getCanonicalPath() : ""); 252 return classPath.toString(); 253 } 254 255 /** 256 * Construct executable module name for java. It is fixed for explicit 257 * module type while it is same as jar file name for automated module type. 258 */ 259 public String getModuleName(MODULE_TYPE moduleType, 260 Path jarPath, String mName) { 261 String jarName = jarPath.toFile().getName(); 262 return (moduleType == MODULE_TYPE.EXPLICIT) ? mName 263 : ((moduleType == MODULE_TYPE.AUTO) ? jarName.substring(0, 264 jarName.indexOf(JAR_EXTN)) : ""); 265 } 266 267 /** 268 * Delete all the files inside the base module path. 269 */ 270 public void cleanModuleBasePath(Path mBasePath) { 271 Arrays.asList(mBasePath.toFile().listFiles()).forEach(f -> { 272 System.out.println("delete: " + f); 273 f.delete(); 274 }); 275 } 276 277 } |