e2a0ff3e0d
Reviewed-by: alanb, ksrini, henryjen, sundar
280 lines
10 KiB
Java
280 lines
10 KiB
Java
/*
|
|
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.io.File;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.StringJoiner;
|
|
import java.util.Arrays;
|
|
import java.util.stream.Collectors;
|
|
import java.lang.module.ModuleDescriptor;
|
|
import jdk.testlibrary.OutputAnalyzer;
|
|
import org.testng.annotations.DataProvider;
|
|
import org.testng.annotations.Test;
|
|
import jdk.internal.module.ModuleInfoWriter;
|
|
import static java.lang.module.ModuleDescriptor.Builder;
|
|
|
|
/**
|
|
* Base class need to be extended by modular test for security.
|
|
*/
|
|
public abstract class ModularTest {
|
|
|
|
/**
|
|
* Enum represents all supported module types supported in JDK9. i.e.
|
|
* EXPLICIT - Modules have module descriptor(module-info.java)
|
|
* defining the module.
|
|
* AUTO - Are regular jar files but provided in MODULE_PATH instead
|
|
* of CLASS_PATH.
|
|
* UNNAMED - Are regular jar but provided through CLASS_PATH.
|
|
*/
|
|
public enum MODULE_TYPE {
|
|
|
|
EXPLICIT, AUTO, UNNAMED;
|
|
}
|
|
|
|
public static final String SPACE = " ";
|
|
public static final Path SRC = Paths.get(System.getProperty("test.src"));
|
|
public static final String DESCRIPTOR = "MetaService";
|
|
public static final String MODULAR = "Modular";
|
|
public static final String AUTO = "AutoServiceType";
|
|
public static final String JAR_EXTN = ".jar";
|
|
|
|
/**
|
|
* Setup test data for the test.
|
|
*/
|
|
@DataProvider(name = "TestParams")
|
|
public Object[][] setUpTestData() {
|
|
return getTestInput();
|
|
}
|
|
|
|
/**
|
|
* Test method for TestNG.
|
|
*/
|
|
@Test(dataProvider = "TestParams")
|
|
public void runTest(MODULE_TYPE cModuleType, MODULE_TYPE sModuletype,
|
|
boolean addMetaDesc, String failureMsgExpected, String[] args)
|
|
throws Exception {
|
|
|
|
String testName = new StringJoiner("_").add(cModuleType.toString())
|
|
.add(sModuletype.toString()).add(
|
|
(addMetaDesc) ? "WITH_SERVICE" : "NO_SERVICE")
|
|
.toString();
|
|
|
|
System.out.format("%nStarting Test case: '%s'", testName);
|
|
Path cJarPath = findJarPath(false, cModuleType, false,
|
|
(sModuletype == MODULE_TYPE.EXPLICIT));
|
|
Path sJarPath = findJarPath(true, sModuletype, addMetaDesc, false);
|
|
System.out.format("%nClient jar path : %s ", cJarPath);
|
|
System.out.format("%nService jar path : %s ", sJarPath);
|
|
OutputAnalyzer output = executeTestClient(cModuleType, cJarPath,
|
|
sModuletype, sJarPath, args);
|
|
|
|
if (output.getExitValue() != 0) {
|
|
if (failureMsgExpected != null
|
|
&& output.getOutput().contains(failureMsgExpected)) {
|
|
System.out.println("PASS: Test is expected to fail here.");
|
|
} else {
|
|
System.out.format("%nUnexpected failure occured with exit code"
|
|
+ " '%s'.", output.getExitValue());
|
|
throw new RuntimeException("Unexpected failure occured.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Abstract method need to be implemented by each Test type to provide
|
|
* test parameters.
|
|
*/
|
|
public abstract Object[][] getTestInput();
|
|
|
|
/**
|
|
* Execute the test client to access required service.
|
|
*/
|
|
public abstract OutputAnalyzer executeTestClient(MODULE_TYPE cModuleType,
|
|
Path cJarPath, MODULE_TYPE sModuletype, Path sJarPath,
|
|
String... args) throws Exception;
|
|
|
|
/**
|
|
* Find the Jar for service/client based on module type and if service
|
|
* descriptor required.
|
|
*/
|
|
public abstract Path findJarPath(boolean service, MODULE_TYPE moduleType,
|
|
boolean addMetaDesc, boolean dependsOnServiceModule);
|
|
|
|
/**
|
|
* Constructs a Java Command line string based on modular structure followed
|
|
* by modular client and service.
|
|
*/
|
|
public String[] getJavaCommand(Path modulePath, String classPath,
|
|
String clientModuleName, String mainClass,
|
|
Map<String, String> vmArgs, String... options) throws IOException {
|
|
|
|
final StringJoiner command = new StringJoiner(SPACE, SPACE, SPACE);
|
|
vmArgs.forEach((key, value) -> command.add(key + value));
|
|
if (modulePath != null) {
|
|
command.add("--module-path").add(modulePath.toFile().getCanonicalPath());
|
|
}
|
|
if (classPath != null && classPath.length() > 0) {
|
|
command.add("-cp").add(classPath);
|
|
}
|
|
if (clientModuleName != null && clientModuleName.length() > 0) {
|
|
command.add("-m").add(clientModuleName + "/" + mainClass);
|
|
} else {
|
|
command.add(mainClass);
|
|
}
|
|
command.add(Arrays.stream(options).collect(Collectors.joining(SPACE)));
|
|
return command.toString().trim().split("[\\s]+");
|
|
}
|
|
|
|
/**
|
|
* Generate ModuleDescriptor object for explicit/auto based client/Service
|
|
* modules type.
|
|
*/
|
|
public ModuleDescriptor generateModuleDescriptor(boolean isService,
|
|
MODULE_TYPE moduleType, String moduleName, String pkg,
|
|
String serviceInterface, String serviceImpl,
|
|
String serviceModuleName, List<String> requiredModules,
|
|
boolean depends) {
|
|
|
|
final Builder builder;
|
|
if (moduleType == MODULE_TYPE.EXPLICIT) {
|
|
System.out.format(" %nGenerating ModuleDescriptor object");
|
|
builder = new Builder(moduleName).exports(pkg);
|
|
if (isService && serviceInterface != null && serviceImpl != null) {
|
|
builder.provides(serviceInterface, serviceImpl);
|
|
} else {
|
|
if (serviceInterface != null) {
|
|
builder.uses(serviceInterface);
|
|
}
|
|
if (depends) {
|
|
builder.requires(serviceModuleName);
|
|
}
|
|
}
|
|
} else {
|
|
System.out.format(" %nModuleDescriptor object not required.");
|
|
return null;
|
|
}
|
|
requiredModules.stream().forEach(reqMod -> builder.requires(reqMod));
|
|
return builder.build();
|
|
}
|
|
|
|
/**
|
|
* Generates service descriptor inside META-INF folder.
|
|
*/
|
|
public boolean createMetaInfServiceDescriptor(
|
|
Path serviceDescriptorFile, String serviceImpl) {
|
|
boolean created = true;
|
|
System.out.format("%nCreating META-INF service descriptor for '%s' "
|
|
+ "at path '%s'", serviceImpl, serviceDescriptorFile);
|
|
try {
|
|
Path parent = serviceDescriptorFile.getParent();
|
|
if (parent != null) {
|
|
Files.createDirectories(parent);
|
|
}
|
|
Files.write(serviceDescriptorFile, serviceImpl.getBytes("UTF-8"));
|
|
System.out.println(
|
|
"META-INF service descriptor generated successfully");
|
|
} catch (IOException e) {
|
|
e.printStackTrace(System.out);
|
|
created = false;
|
|
}
|
|
return created;
|
|
}
|
|
|
|
/**
|
|
* Generate modular/regular jar file.
|
|
*/
|
|
public void generateJar(ModuleDescriptor mDescriptor, Path jar,
|
|
Path compilePath) throws IOException {
|
|
System.out.format("%nCreating jar file '%s'", jar);
|
|
JarUtils.createJarFile(jar, compilePath);
|
|
if (mDescriptor != null) {
|
|
Path dir = Files.createTempDirectory("tmp");
|
|
Path mi = dir.resolve("module-info.class");
|
|
try (OutputStream out = Files.newOutputStream(mi)) {
|
|
ModuleInfoWriter.write(mDescriptor, out);
|
|
}
|
|
System.out.format("%nAdding 'module-info.class' to jar '%s'", jar);
|
|
JarUtils.updateJarFile(jar, dir);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy pre-generated jar files to the module base path.
|
|
*/
|
|
public void copyJarsToModuleBase(MODULE_TYPE moduleType, Path jar,
|
|
Path mBasePath) throws IOException {
|
|
if (mBasePath != null) {
|
|
Files.createDirectories(mBasePath);
|
|
}
|
|
if (moduleType != MODULE_TYPE.UNNAMED) {
|
|
Path artifactName = mBasePath.resolve(jar.getFileName());
|
|
System.out.format("%nCopy jar path: '%s' to module base path: %s",
|
|
jar, artifactName);
|
|
Files.copy(jar, artifactName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Construct class path string.
|
|
*/
|
|
public String buildClassPath(MODULE_TYPE cModuleType,
|
|
Path cJarPath, MODULE_TYPE sModuletype,
|
|
Path sJarPath) throws IOException {
|
|
StringJoiner classPath = new StringJoiner(File.pathSeparator);
|
|
classPath.add((cModuleType == MODULE_TYPE.UNNAMED)
|
|
? cJarPath.toFile().getCanonicalPath() : "");
|
|
classPath.add((sModuletype == MODULE_TYPE.UNNAMED)
|
|
? sJarPath.toFile().getCanonicalPath() : "");
|
|
return classPath.toString();
|
|
}
|
|
|
|
/**
|
|
* Construct executable module name for java. It is fixed for explicit
|
|
* module type while it is same as jar file name for automated module type.
|
|
*/
|
|
public String getModuleName(MODULE_TYPE moduleType,
|
|
Path jarPath, String mName) {
|
|
String jarName = jarPath.toFile().getName();
|
|
return (moduleType == MODULE_TYPE.EXPLICIT) ? mName
|
|
: ((moduleType == MODULE_TYPE.AUTO) ? jarName.substring(0,
|
|
jarName.indexOf(JAR_EXTN)) : null);
|
|
}
|
|
|
|
/**
|
|
* Delete all the files inside the base module path.
|
|
*/
|
|
public void cleanModuleBasePath(Path mBasePath) {
|
|
Arrays.asList(mBasePath.toFile().listFiles()).forEach(f -> {
|
|
System.out.println("delete: " + f);
|
|
f.delete();
|
|
});
|
|
}
|
|
|
|
}
|