jdk/test/java/security/modules/ModularTest.java
author alanb
Thu, 01 Dec 2016 08:57:53 +0000
changeset 42338 a60f280f803c
parent 40261 86a49ba76f52
child 43712 5dfd0950317c
permissions -rw-r--r--
8169069: Module system implementation refresh (11/2016) Reviewed-by: plevart, chegar, psandoz, mchung, alanb, dfuchs, naoto, coffeys, weijun Contributed-by: alan.bateman@oracle.com, mandy.chung@oracle.com, claes.redestad@oracle.com, mark.reinhold@oracle.com

/*
 * 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 = ModuleDescriptor.module(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();
        });
    }

}