/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.test.manualmode.deployment;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXTENSION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.helpers.Operations;
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.test.deployment.trivial.ServiceActivatorDeploymentUtil;
import org.jboss.as.test.shared.TimeoutUtil;
import org.jboss.byteman.agent.submit.Submit;
import org.jboss.dmr.ModelNode;
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.BeforeClass;
import org.junit.Ignore;
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.UnsuccessfulOperationException;
import org.wildfly.core.testrunner.WildflyTestRunner;
/**
* Tests server reload during a deployment or undeployment to a wildfly core server by the filesystem scanner.
*
* @author <a href="mailto:ehugonne@redhat.com">Emmanuel Hugonnet</a> (c) 2015 Red Hat, inc.
*/
@RunWith(WildflyTestRunner.class)
@ServerControl(manual = true)
@Ignore("this test relies too much on hacking internals (see the byteman script) so it's only here to allow " +
"custom use if there is a need to diagnose a problem in this area")
public class DeploymentScannerShutdownTestCase {
// Max time to wait for some action to complete, in ms
private static final int TIMEOUT = TimeoutUtil.adjust(20000);
// Pause time between checks whether some action has completed, in ms
private static final int BACKOFF = 10;
private static final String DEPLOYMENT_SCANNER_EXTENSION = "org.jboss.as.deployment-scanner";
private static final String DEPLOYMENT_SCANNER_SUBSYSTEM = "deployment-scanner";
private static final String DEPLOYMENT_NAME = "test-deployment.jar";
@Inject
private ServerController container;
private File deployDir;
private final String scannerName = "autoZips";
private static final Map<String, String> properties = new HashMap<>();
private final Submit bytemanSubmit = new Submit(
System.getProperty("byteman.server.ipaddress", Submit.DEFAULT_ADDRESS),
Integer.getInteger("byteman.server.port", Submit.DEFAULT_PORT));
@BeforeClass
public static void addDeploymentScanner() throws Exception {
properties.clear();
properties.put("service", "is new");
properties.clear();
properties.put("service", "is replaced");
}
@After
public void cleanAll() throws Exception {
try {
removeRules();
} catch (Exception e) {
}
try {
removeDeploymentScanner(scannerName);
} catch (Exception e) {
}
try {
removeDeploymentScannerExtension();
} catch (Exception e) {
}
cleanFile(deployDir);
deployDir.delete();
container.stop();
}
@Before
public void prepareServer() throws Exception {
deployDir = createDeploymentDir("auto-deployments");
container.start();
addDeploymentScannerExtension();
addDeploymentScanner(deployDir, scannerName, true);
}
private void deployRules() throws Exception {
bytemanSubmit.addRulesFromResources(Collections.singletonList(
DeploymentScannerShutdownTestCase.class.getClassLoader().getResourceAsStream("byteman/DeploymentScannerShutdownTestCase.btm")));
}
private void removeRules() {
try {
bytemanSubmit.deleteAllRules();
} catch (Exception ex) {
}
}
@Test
public void testFilesystemDeployment() throws Exception {
deployRules();
final JavaArchive archive = ServiceActivatorDeploymentUtil.createServiceActivatorDeploymentArchive(DEPLOYMENT_NAME, properties);
final File dir = new File("target/archives");
dir.mkdirs();
final File deployed = new File(deployDir, DEPLOYMENT_NAME + ".deployed");
final File target = new File(deployDir, DEPLOYMENT_NAME);
final File file = new File(dir, DEPLOYMENT_NAME);
archive.as(ZipExporter.class).exportTo(file, true);
deploy(file, target, deployed);
container.reload(TIMEOUT);
Assert.assertTrue("We should have the deployed marker", deployed.exists());
Assert.assertTrue(container.isStarted());
Path serverLog = getAbsoluteLogFilePath("jboss.server.log.dir", "server.log");
assertLogContains(serverLog, "rejected from java.util.concurrent.ScheduledThreadPoolExecutor", false);
container.stop();
deployed.delete();
target.delete();
container.start();
removeRules();
}
@Test
public void testFileSystemUndeployment() throws Exception {
final JavaArchive archive = ServiceActivatorDeploymentUtil.createServiceActivatorDeploymentArchive("test-deployment.jar", properties);
final File dir = new File("target/archives");
dir.mkdirs();
final File deployed = new File(deployDir, DEPLOYMENT_NAME + ".deployed");
final File undeployed = new File(deployDir, DEPLOYMENT_NAME + ".undeployed");
if (undeployed.exists()) {
undeployed.delete();
}
final File target = new File(deployDir, DEPLOYMENT_NAME);
final File file = new File(dir,DEPLOYMENT_NAME);
archive.as(ZipExporter.class).exportTo(file, true);
removeRules();
deploy(file, target, deployed);
deployRules();
undeploy(target, undeployed);
container.reload(TIMEOUT);
Assert.assertTrue("We should have the undeployed marker", undeployed.exists());
Assert.assertTrue(container.isStarted());
Path serverLog = getAbsoluteLogFilePath("jboss.server.log.dir", "server.log");
assertLogContains(serverLog, "rejected from java.util.concurrent.ScheduledThreadPoolExecutor", false);
//container.start();
}
private void assertLogContains(final Path logFile, final String msg, final boolean expected) throws Exception {
try (final BufferedReader reader = Files.newBufferedReader(logFile, StandardCharsets.UTF_8)) {
String line;
boolean logFound = false;
while ((line = reader.readLine()) != null) {
if (line.contains(msg)) {
logFound = true;
break;
}
}
Assert.assertTrue(logFound == expected);
}
}
private void deploy(final File file, final File target, final File deployed) throws IOException {
Assert.assertFalse(target.exists());
// Copy file to deploy directory
final InputStream in = new BufferedInputStream(new FileInputStream(file));
try {
final OutputStream out = new BufferedOutputStream(new FileOutputStream(target));
try {
int i = in.read();
while (i != -1) {
out.write(i);
i = in.read();
}
} finally {
StreamUtils.safeClose(out);
}
} finally {
StreamUtils.safeClose(in);
}
Assert.assertTrue(file.exists());
waitForMarkerFile(deployed);
}
private void undeploy(final File target, final File undeployed) throws IOException {
// Delete file from deploy directory
target.delete();
waitForMarkerFile(undeployed);
}
private void waitForMarkerFile(File marker) {
for (int i = 0; i < TIMEOUT / BACKOFF; i++) {
if (marker.exists()) {
break;
}
try {
Thread.sleep(BACKOFF);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}
private ModelNode addDeploymentScanner(final File deployDir, final String scannerName, final boolean autoDeployZipped)
throws Exception {
ModelNode add = new ModelNode();
add.get(OP).set(ADD);
ModelNode addr = new ModelNode();
addr.add("subsystem", "deployment-scanner");
addr.add("scanner", scannerName);
add.get(OP_ADDR).set(addr);
add.get("path").set(deployDir.getAbsolutePath());
add.get("scan-enabled").set(true);
add.get("scan-interval").set(1000);
if (autoDeployZipped == false) {
add.get("auto-deploy-zipped").set(false);
}
return container.getClient().executeForResult(add);
}
private void removeDeploymentScanner(final String scannerName) throws Exception {
ModelNode addr = new ModelNode();
addr.add("subsystem", "deployment-scanner");
addr.add("scanner", scannerName);
ModelNode remove = new ModelNode();
remove.get(OP).set(REMOVE);
remove.get(OP_ADDR).set(addr);
container.getClient().executeForResult(remove);
}
private File createDeploymentDir(String dir) {
deployDir = new File("target", dir);
cleanFile(deployDir);
deployDir.mkdirs();
Assert.assertTrue(deployDir.exists());
return deployDir;
}
private static void cleanFile(File toClean) {
if (toClean.isDirectory()) {
for (File child : toClean.listFiles()) {
cleanFile(child);
}
}
toClean.delete();
}
private Path getAbsoluteLogFilePath(final String relativePath, final String fileName) throws UnsuccessfulOperationException {
final ModelNode address = PathAddress.pathAddress(
PathElement.pathElement(ModelDescriptionConstants.PATH, relativePath)).toModelNode();
final ModelNode result;
try {
final ModelNode op = Operations.createReadAttributeOperation(address, ModelDescriptionConstants.PATH);
result = container.getClient().executeForResult(op);
return Paths.get(result.asString(), fileName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void addDeploymentScannerExtension() throws Exception {
ModelNode addOp = Util.createAddOperation(PathAddress.pathAddress(PathElement.pathElement(EXTENSION, DEPLOYMENT_SCANNER_EXTENSION)));
container.getClient().executeForResult(addOp);
addOp = Util.createAddOperation(PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, DEPLOYMENT_SCANNER_SUBSYSTEM)));
container.getClient().executeForResult(addOp);
}
private void removeDeploymentScannerExtension() throws Exception {
try {
ModelNode removeOp = Util.createRemoveOperation(PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, DEPLOYMENT_SCANNER_SUBSYSTEM)));
container.getClient().executeForResult(removeOp);
} finally {
ModelNode removeOp = Util.createRemoveOperation(PathAddress.pathAddress(PathElement.pathElement(EXTENSION, DEPLOYMENT_SCANNER_EXTENSION)));
container.getClient().executeForResult(removeOp);
}
}
}