org.jboss.as.test.manualmode.deployment.DeploymentScannerUnitTestCase.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.as.test.manualmode.deployment.DeploymentScannerUnitTestCase.java

Source

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014, JBoss Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.as.test.manualmode.deployment;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOY;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ENABLED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FULL_REPLACE_DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INPUT_STREAM_INDEX;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;
import static org.junit.Assert.assertEquals;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.function.Supplier;

import javax.inject.Inject;

import org.apache.commons.io.FileUtils;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.client.OperationBuilder;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.protocol.StreamUtils;
import org.jboss.as.repository.PathUtil;
import org.jboss.as.server.deployment.DeploymentUndeployHandler;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.as.test.shared.TimeoutUtil;
import org.jboss.dmr.ModelNode;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.core.testrunner.ServerControl;
import org.wildfly.core.testrunner.ServerController;
import org.wildfly.core.testrunner.WildflyTestRunner;

/**
 * @author Emanuel Muckenhuber
 */
@RunWith(WildflyTestRunner.class)
@ServerControl(manual = true)
public class DeploymentScannerUnitTestCase extends AbstractDeploymentUnitTestCase {

    private static final String JAR_ONE = "deployment-startup-one.jar";
    private static final String JAR_TWO = "deployment-startup-two.jar";
    private static final PathAddress DEPLOYMENT_ONE = PathAddress.pathAddress(DEPLOYMENT, JAR_ONE);
    private static final PathAddress DEPLOYMENT_TWO = PathAddress.pathAddress(DEPLOYMENT, JAR_TWO);
    private static final int TIMEOUT = TimeoutUtil.adjust(30000);
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss,SSS");

    @SuppressWarnings("unused")
    @Inject
    private ServerController container;

    private ModelControllerClient client;

    private static Path deployDir;

    @Before
    public void before() throws IOException {
        deployDir = Files.createTempDirectory("deployment-test-" + UUID.randomUUID());
        if (Files.exists(deployDir)) {
            PathUtil.deleteRecursively(deployDir);
        }
        Files.createDirectories(deployDir);
    }

    @After
    public void after() throws IOException {
        FileUtils.deleteDirectory(deployDir.toFile());
    }

    @Test
    public void testStartup() throws Exception {
        final Path oneDeployed = deployDir.resolve(JAR_ONE + ".deployed");
        final Path twoFailed = deployDir.resolve(JAR_TWO + ".failed");
        container.start();
        try {
            client = TestSuiteEnvironment.getModelControllerClient();
            //set the logging to debug
            addDebugDeploymentLogger();
            try {
                final Path deploymentOne = deployDir.resolve(JAR_ONE);
                final Path deploymentTwo = deployDir.resolve(JAR_TWO);

                createDeployment(deploymentOne, "org.jboss.modules");
                createDeployment(deploymentTwo, "non.existing.dependency");
                addDeploymentScanner(0, false);
                try {
                    // Wait until deployed ...
                    long timeout = System.currentTimeMillis() + TIMEOUT;
                    while (!(exists(DEPLOYMENT_ONE) && exists(DEPLOYMENT_TWO))
                            && System.currentTimeMillis() < timeout) {
                        Thread.sleep(100);
                    }
                    Assert.assertTrue(exists(DEPLOYMENT_ONE));
                    Assert.assertEquals("OK", deploymentState(DEPLOYMENT_ONE));
                    Assert.assertTrue(exists(DEPLOYMENT_TWO));
                    Assert.assertEquals("FAILED", deploymentState(DEPLOYMENT_TWO));
                    Assert.assertTrue(Files.exists(oneDeployed));
                    Assert.assertTrue(Files.exists(twoFailed));

                    ModelNode rollBackOnBoot = Util.getWriteAttributeOperation(
                            PathAddress.parseCLIStyleAddress("/subsystem=deployment-scanner/scanner=testScanner"),
                            "runtime-failure-causes-rollback", true);
                    ModelNode result = executeOperation(rollBackOnBoot);
                    assertEquals("Unexpected outcome of rollbacking the test deployment scanner: " + rollBackOnBoot,
                            ModelDescriptionConstants.SUCCESS, result.get(OUTCOME).asString());

                    // Restart ...
                    client.close();
                    container.stop();
                    container.start();
                    client = TestSuiteEnvironment.getModelControllerClient();

                    // Wait until started ...
                    timeout = System.currentTimeMillis() + TIMEOUT;
                    while (!isRunning() && System.currentTimeMillis() < timeout) {
                        Thread.sleep(10);
                    }

                    Assert.assertTrue(Files.exists(oneDeployed));
                    Assert.assertTrue(Files.exists(twoFailed));

                    Assert.assertTrue(exists(DEPLOYMENT_ONE));
                    Assert.assertEquals("OK", deploymentState(DEPLOYMENT_ONE));

                    timeout = System.currentTimeMillis() + TIMEOUT;
                    while (exists(DEPLOYMENT_TWO) && System.currentTimeMillis() < timeout) {
                        Thread.sleep(10);
                    }
                    Assert.assertFalse(
                            "Deployment two should exist at " + TIME_FORMATTER.format(LocalDateTime.now()),
                            exists(DEPLOYMENT_TWO));
                    ModelNode disableScanner = Util.getWriteAttributeOperation(
                            PathAddress.parseCLIStyleAddress("/subsystem=deployment-scanner/scanner=testScanner"),
                            "scan-interval", 300000);
                    result = executeOperation(disableScanner);
                    assertEquals("Unexpected outcome of disabling the test deployment scanner: " + disableScanner,
                            ModelDescriptionConstants.SUCCESS, result.get(OUTCOME).asString());

                    final ModelNode undeployOp = Util.getEmptyOperation(DeploymentUndeployHandler.OPERATION_NAME,
                            DEPLOYMENT_ONE.toModelNode());
                    result = executeOperation(undeployOp);
                    assertEquals("Unexpected outcome of undeploying deployment one: " + undeployOp,
                            ModelDescriptionConstants.SUCCESS, result.get(OUTCOME).asString());
                    Assert.assertTrue(exists(DEPLOYMENT_ONE));
                    Assert.assertEquals("STOPPED", deploymentState(DEPLOYMENT_ONE));

                    timeout = System.currentTimeMillis() + TIMEOUT;

                    while (Files.exists(oneDeployed) && System.currentTimeMillis() < timeout) {
                        Thread.sleep(10);
                    }
                    Assert.assertFalse(Files.exists(oneDeployed));
                } finally {
                    removeDeploymentScanner();
                    removeDebugDeploymentLogger();
                }

            } finally {
                StreamUtils.safeClose(client);
            }
        } finally {
            container.stop();
        }
    }

    /**
     * https://bugzilla.redhat.com/show_bug.cgi?id=1291710
     *
     * When FS deployment failed during boot, persistent deployments were removed too.
     */
    @Test
    public void testFailedDeploymentWithPersistentDeployment() throws Exception {
        container.start();
        try {
            client = TestSuiteEnvironment.getModelControllerClient();
            try {
                // deploy a persistent deployment

                Path persistentDeploymentPath = deployDir.resolve(JAR_ONE);
                PathAddress persistentDeploymentAddress = PathAddress.pathAddress(DEPLOYMENT, JAR_ONE);
                Archive<?> validDeployment = createDeploymentArchive();
                deployPersistent(JAR_ONE, validDeployment);
                Assert.assertTrue(String.format("%s not deployed", persistentDeploymentPath),
                        exists(persistentDeploymentAddress));

                // deploy an invalid file-system deployment

                addDeploymentScanner(0, true);
                try {
                    container.stop();
                    createDeployment(deployDir.resolve(JAR_TWO), "not.existing.dependency");
                    container.start();

                    Path failedMarker = deployDir.resolve(JAR_TWO + ".failed");
                    waitFor(String.format("Missing .failed marker for %s", JAR_TWO),
                            () -> Files.exists(failedMarker));
                    waitFor(String.format("%s should be deployed", JAR_ONE),
                            () -> exists(persistentDeploymentAddress));
                    waitFor(String.format("%s should not be deployed", JAR_TWO),
                            () -> !exists(PathAddress.pathAddress(DEPLOYMENT, JAR_TWO)));
                } finally {
                    removeDeploymentScanner();
                    client.execute(Util.createRemoveOperation(persistentDeploymentAddress));
                }
            } finally {
                StreamUtils.safeClose(client);
            }
        } finally {
            container.stop();
        }
    }

    /**
         * https://issues.jboss.org/browse/WFCORE-1890
         *
         * When FS deployment is erplaced with a managed deployment with same name it is not marked as undeployed and reboot will fail.
         */
    @Test
    public void testReplaceDeploymentWithPersistentDeployment() throws Exception {
        container.start();
        try {
            client = TestSuiteEnvironment.getModelControllerClient();
            try {
                final PathAddress persistentDeploymentAddress = PathAddress.pathAddress(DEPLOYMENT, JAR_ONE);
                addDeploymentScanner(0, true);
                try {
                    // deploy an file-system deployment
                    container.stop();
                    createDeployment(deployDir.resolve(JAR_ONE), "org.jboss.modules");
                    container.start();
                    Path deployedMarker = deployDir.resolve(JAR_ONE + ".deployed");
                    waitFor(String.format("Missing .deployed marker for %s", JAR_ONE),
                            () -> Files.exists(deployedMarker));
                    Assert.assertTrue(String.format("%s should be deployed", JAR_ONE),
                            exists(persistentDeploymentAddress));
                    //Replace deployment
                    Archive<?> validDeployment = createDeploymentArchive();
                    replaceWithPersistent(JAR_ONE, validDeployment);
                    Assert.assertTrue(String.format("%s should be deployed", JAR_ONE),
                            exists(persistentDeploymentAddress));
                    waitFor(String.format("Missing .undeployed marker for %s", JAR_ONE),
                            () -> Files.exists(deployDir.resolve(JAR_ONE + ".undeployed")));
                } finally {
                    removeDeploymentScanner();
                    client.execute(Util.createRemoveOperation(persistentDeploymentAddress));
                }
            } finally {
                StreamUtils.safeClose(client);
            }
        } finally {
            container.stop();
        }
    }

    /**
     * https://bugzilla.redhat.com/show_bug.cgi?id=997583
     *
     * FS deployments that failed during boot were not removed.
     */
    @Test
    public void testFailedFileSystemDeploymentDuringBoot() throws Exception {
        container.start();
        try {
            client = TestSuiteEnvironment.getModelControllerClient();

            addDeploymentScanner(0, true);
            try {
                container.stop();

                createDeployment(deployDir.resolve(JAR_ONE), "not.existing.dependency");
                container.start();
                waitFor(String.format("Failed marker was not created for %s", JAR_ONE),
                        () -> Files.exists(deployDir.resolve(JAR_ONE + ".failed")));
                waitFor(String.format("%s should not be deployed", JAR_ONE),
                        () -> !exists(PathAddress.pathAddress(DEPLOYMENT, JAR_ONE)));
            } finally {
                removeDeploymentScanner();
            }
        } finally {
            StreamUtils.safeClose(client);
            container.stop();
        }
    }

    @Test
    public void testFailedDeploymentWithCorrectDeploymentDuringBoot() throws Exception {
        container.start();
        try {
            client = TestSuiteEnvironment.getModelControllerClient();

            addDeploymentScanner(0, true);
            try {
                container.stop();

                createDeployment(deployDir.resolve(JAR_ONE), "not.existing.dependency");
                createDeployment(deployDir.resolve(JAR_TWO), "org.jboss.modules");

                container.start();
                waitFor(String.format("Failed marker was not created for %s", JAR_ONE),
                        () -> Files.exists(deployDir.resolve(JAR_ONE + ".failed")));
                waitFor(String.format("%s should not be deployed", JAR_ONE),
                        () -> !exists(PathAddress.pathAddress(DEPLOYMENT, JAR_ONE)));
                waitFor(String.format("%s should be deployed", JAR_TWO),
                        () -> exists(PathAddress.pathAddress(DEPLOYMENT, JAR_TWO)));
            } finally {
                removeDeploymentScanner();
            }
        } finally {
            StreamUtils.safeClose(client);
            container.stop();
        }
    }

    private void addDebugDeploymentLogger() throws Exception {
        boolean ok = false;
        try {
            final ModelNode op = Util.createAddOperation(getScannerLoggerResourcePath());
            op.get("category").set("org.jboss.as.server.deployment.scanner");
            op.get("level").set("TRACE");
            op.get("use-parent-handlers").set(true);
            ModelNode result = executeOperation(op);
            assertEquals("Unexpected outcome of setting the test deployment logger to debug: " + op, SUCCESS,
                    result.get(OUTCOME).asString());
            ok = true;
        } finally {
            if (!ok) {
                ModelNode removeOp = Util.createRemoveOperation(getScannerLoggerResourcePath());
                ModelNode result = executeOperation(removeOp);
                assertEquals("Unexpected outcome of removing the test deployment logger: " + removeOp,
                        ModelDescriptionConstants.SUCCESS, result.get(OUTCOME).asString());
            }
        }
    }

    private void removeDebugDeploymentLogger() throws Exception {
        ModelNode removeOp = Util.createRemoveOperation(getScannerLoggerResourcePath());
        ModelNode result = executeOperation(removeOp);
        assertEquals("Unexpected outcome of removing the test deployment logger: " + result, SUCCESS,
                result.get(OUTCOME).asString());
    }

    private PathAddress getScannerLoggerResourcePath() {
        return PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, "logging"),
                PathElement.pathElement("logger", "org.jboss.as.server.deployment.scanner"));
    }

    private Archive<?> createDeploymentArchive() {
        final JavaArchive archive = ShrinkWrap.create(JavaArchive.class);
        final String dependencies = "Dependencies: org.jboss.modules";
        archive.add(new StringAsset(dependencies), "META-INF/MANIFEST.MF");
        return archive;
    }

    private void waitFor(String message, ExceptionWrappingSupplier<Boolean> condition) throws Exception {
        long timeout = System.currentTimeMillis() + TimeoutUtil.adjust(TIMEOUT);
        while (!condition.get() && System.currentTimeMillis() < timeout) {
            Thread.sleep(100);
        }
        Assert.assertTrue(message, condition.get());
    }

    /**
     * Creates managed deployment
     *
     * @param name deployment runtime name
     * @param archive archive to deploy
     */
    private void deployPersistent(String name, Archive archive) throws IOException {
        PathAddress address = PathAddress.pathAddress(DEPLOYMENT, name);

        ModelNode operation = Util.createOperation(ADD, address);
        operation.get(CONTENT).get(0).get(INPUT_STREAM_INDEX).set(0);
        OperationBuilder ob = new OperationBuilder(operation, true);
        ob.addInputStream(archive.as(ZipExporter.class).exportAsInputStream());
        ModelNode result = client.execute(ob.build());
        Assert.assertEquals(SUCCESS, result.get(OUTCOME).asString());

        operation = Util.createOperation(DEPLOY, address);
        result = client.execute(operation);
        Assert.assertEquals(SUCCESS, result.get(OUTCOME).asString());
    }

    private void replaceWithPersistent(String name, Archive archive) throws IOException {
        ModelNode operation = Util.createOperation(FULL_REPLACE_DEPLOYMENT, PathAddress.EMPTY_ADDRESS);
        operation.get(CONTENT).get(0).get(INPUT_STREAM_INDEX).set(0);
        operation.get(ENABLED).set(true);
        operation.get(NAME).set(name);
        OperationBuilder ob = new OperationBuilder(operation, true);
        ob.addInputStream(archive.as(ZipExporter.class).exportAsInputStream());
        ModelNode result = client.execute(ob.build());
        Assert.assertEquals(result.toString(), SUCCESS, result.get(OUTCOME).asString());
    }

    @Override
    protected ModelNode executeOperation(ModelNode op) throws IOException {
        return client.execute(op);
    }

    @Override
    protected File getDeployDir() {
        return deployDir.toFile();
    }

    /**
     * A Supplier that wraps eventual checked exception into runtime exception.
     *
     * @param <T> the result type
     */
    @FunctionalInterface
    private interface ExceptionWrappingSupplier<T> extends Supplier<T> {
        @Override
        default T get() {
            try {
                return throwingGet();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        T throwingGet() throws Exception;
    }

}