/* * Copyright (C) 2016 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 library 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package org.wildfly.core.test.standalone.mgmt.api; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT; import static org.jboss.as.controller.client.helpers.ClientConstants.OP; import static org.jboss.as.controller.client.helpers.ClientConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UUID; import static org.jboss.as.repository.PathUtil.deleteRecursively; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.inject.Inject; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.OperationBuilder; import org.jboss.as.controller.client.OperationResponse; import org.jboss.as.controller.client.helpers.ClientConstants; import org.jboss.as.controller.client.helpers.Operations; import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentManager; import org.jboss.as.test.deployment.trivial.ServiceActivatorDeployment; import org.jboss.as.test.deployment.trivial.ServiceActivatorDeploymentUtil; import org.jboss.as.test.shared.TimeoutUtil; import org.jboss.dmr.ModelNode; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.wildfly.core.testrunner.ManagementClient; import org.wildfly.core.testrunner.WildflyTestRunner; /** * * @author <a href="mailto:ehugonne@redhat.com">Emmanuel Hugonnet</a> (c) 2015 Red Hat, inc. */ @RunWith(WildflyTestRunner.class) public class ExplodedDeploymentTestCase { // Max time to wait for some action to complete, in ms private static final int TIMEOUT = TimeoutUtil.adjust(20000); @Inject private ManagementClient managementClient; private static final Properties properties = new Properties(); private static final Properties properties2 = new Properties(); private static final Properties properties3 = new Properties(); @BeforeClass public static void clearProperties() throws Exception { properties.clear(); properties.put("service", "is new"); properties2.clear(); properties2.put("service", "is added"); properties3.clear(); properties3.put("service", "is replaced"); } @AfterClass public static void cleanFiles() throws IOException { String jbossBaseDir = System.getProperty("jboss.home"); Assert.assertNotNull(jbossBaseDir); Path dataDir = new File(jbossBaseDir).toPath().resolve("standalone").resolve("data"); Assert.assertTrue(Files.exists(dataDir)); deleteRecursively(dataDir.resolve("managed-exploded")); } @Test public void testDeploymentArchive() throws Exception { final JavaArchive archive = ServiceActivatorDeploymentUtil.createServiceActivatorDeploymentArchive("test-deployment.jar", properties); final ModelControllerClient client = managementClient.getControllerClient(); final ServerDeploymentManager manager = ServerDeploymentManager.Factory.create(client); testDeployments(false, new ExplodedDeploymentExecutor() { @Override public void initialDeploy() throws IOException { try (InputStream is = archive.as(ZipExporter.class).exportAsInputStream()) { Future<?> future = manager.execute(manager.newDeploymentPlan() .add("test-deployment.jar", is) .explodeDeployment("test-deployment.jar") .deploy("test-deployment.jar") .build()); awaitDeploymentExecution(future); } } @Override public void addContent() throws IOException { String content = ""; try (StringWriter writer = new StringWriter()) { properties2.store(writer, "New Content"); content = writer.toString(); } try (InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) { Future<?> future = manager.execute(manager.newDeploymentPlan() .addContentToDeployment("test-deployment.jar", Collections.singletonMap("SimpleTest.properties", is)) .redeploy("test-deployment.jar") .build()); awaitDeploymentExecution(future); } } @Override public void replaceContent() throws IOException { String content = ""; try (StringWriter writer = new StringWriter()) { properties3.store(writer, "Replace Content"); content = writer.toString(); } try (InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) { Map<String, InputStream> contents = new HashMap<>(); contents.put("service-activator-deployment.properties", is); Future<?> future = manager.execute(manager.newDeploymentPlan() .addContentToDeployment("test-deployment.jar", contents) .redeploy("test-deployment.jar") .build()); awaitDeploymentExecution(future); } } @Override public void removeContent() throws IOException { Future<?> future = manager.execute(manager.newDeploymentPlan() .removeContenFromDeployment("test-deployment.jar", Collections.singletonList("SimpleTest.properties")) .redeploy("test-deployment.jar") .build()); awaitDeploymentExecution(future); } @Override public void readContent(String path, String expectedValue) throws IOException { ModelNode op = new ModelNode(); op.get(OP).set(ClientConstants.READ_CONTENT_OPERATION); op.get(OP_ADDR).add(DEPLOYMENT, "test-deployment.jar"); op.get(PATH).set(path); Future<OperationResponse> future = client.executeOperationAsync(OperationBuilder.create(op, false).build(), null); try { OperationResponse response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); Assert.assertTrue(Operations.isSuccessfulOutcome(response.getResponseNode())); Assert.assertTrue(Operations.readResult(response.getResponseNode()).hasDefined(UUID)); List<OperationResponse.StreamEntry> streams = response.getInputStreams(); Assert.assertThat(streams, is(notNullValue())); Assert.assertThat(streams.size(), is(1)); try (InputStream in = streams.get(0).getStream()) { Properties content = new Properties(); content.load(in); Assert.assertThat(content.getProperty("service"), is(expectedValue)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } } @Override public void undeploy() { Future<?> future = manager.execute(manager.newDeploymentPlan().undeploy("test-deployment.jar") .remove("test-deployment.jar").build()); awaitDeploymentExecution(future); } @Override public void checkNoContent(String path) throws IOException { ModelNode op = new ModelNode(); op.get(OP).set(ClientConstants.READ_CONTENT_OPERATION); op.get(OP_ADDR).add(DEPLOYMENT, "test-deployment.jar"); op.get(PATH).set(path); Future<OperationResponse> future = client.executeOperationAsync(OperationBuilder.create(op, false).build(), null); try { OperationResponse response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); Assert.assertFalse(Operations.isSuccessfulOutcome(response.getResponseNode())); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } } @Override public void browseContent(String path, List<String> expectedContents) throws IOException { ModelNode op = new ModelNode(); op.get(OP).set(ClientConstants.DEPLOYMENT_BROWSE_CONTENT_OPERATION); op.get(OP_ADDR).add(DEPLOYMENT, "test-deployment.jar"); if (path != null && !path.isEmpty()) { op.get(PATH).set(path); } Future<ModelNode> future = client.executeAsync(OperationBuilder.create(op, false).build(), null); try { ModelNode response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); Assert.assertTrue(Operations.isSuccessfulOutcome(response)); List<ModelNode> contents = Operations.readResult(response).asList(); for (ModelNode content : contents) { Assert.assertTrue(content.hasDefined("path")); String contentPath = content.get("path").asString(); Assert.assertTrue(content.asString() + " isn't expected", expectedContents.contains(contentPath)); Assert.assertTrue(content.hasDefined("directory")); if (!content.get("directory").asBoolean()) { Assert.assertTrue(content.hasDefined("file-size")); } expectedContents.remove(contentPath); } Assert.assertTrue(expectedContents.isEmpty()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } } }); } @Test public void testEmptyDeployment() throws Exception { final ModelControllerClient client = managementClient.getControllerClient(); final ServerDeploymentManager manager = ServerDeploymentManager.Factory.create(client); testDeployments(false, new ExplodedDeploymentExecutor() { @Override public void initialDeploy() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); properties.store(out, ""); Map<String, InputStream> contents = new HashMap<>(); contents.put("META-INF/MANIFEST.MF", new ByteArrayInputStream("Dependencies: org.jboss.msc\n".getBytes(StandardCharsets.UTF_8))); contents.put("META-INF/services/org.jboss.msc.service.ServiceActivator", new ByteArrayInputStream("org.jboss.as.test.deployment.trivial.ServiceActivatorDeployment\n".getBytes(StandardCharsets.UTF_8))); contents.put("org/jboss/as/test/deployment/trivial/ServiceActivatorDeployment.class", ServiceActivatorDeployment.class.getResourceAsStream("ServiceActivatorDeployment.class")); contents.put("service-activator-deployment.properties", new ByteArrayInputStream(out.toByteArray())); Future<?> future = manager.execute(manager.newDeploymentPlan() .add("test-deployment.jar", (InputStream) null) .addContentToDeployment("test-deployment.jar", contents) .deploy("test-deployment.jar") .build()); awaitDeploymentExecution(future); } @Override public void addContent() throws IOException { String content = ""; try (StringWriter writer = new StringWriter()) { properties2.store(writer, "New Content"); content = writer.toString(); } try (InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) { Future<?> future = manager.execute(manager.newDeploymentPlan() .addContentToDeployment("test-deployment.jar", Collections.singletonMap("SimpleTest.properties", is)) .redeploy("test-deployment.jar") .build()); awaitDeploymentExecution(future); } } @Override public void replaceContent() throws IOException { String content = ""; try (StringWriter writer = new StringWriter()) { properties3.store(writer, "Replace Content"); content = writer.toString(); } try (InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) { Future<?> future = manager.execute(manager.newDeploymentPlan() .addContentToDeployment("test-deployment.jar", Collections.singletonMap("service-activator-deployment.properties", is)) .redeploy("test-deployment.jar") .build()); awaitDeploymentExecution(future); } } @Override public void removeContent() throws IOException { Future<?> future = manager.execute(manager.newDeploymentPlan() .removeContenFromDeployment("test-deployment.jar", Collections.singletonList("SimpleTest.properties")) .build()); awaitDeploymentExecution(future); } @Override public void readContent(String path, String expectedValue) throws IOException { ModelNode op = new ModelNode(); op.get(OP).set(ClientConstants.READ_CONTENT_OPERATION); op.get(OP_ADDR).add(DEPLOYMENT, "test-deployment.jar"); op.get(PATH).set(path); Future<OperationResponse> future = client.executeOperationAsync(OperationBuilder.create(op, false).build(), null); try { OperationResponse response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); Assert.assertTrue(Operations.isSuccessfulOutcome(response.getResponseNode())); Assert.assertTrue(Operations.readResult(response.getResponseNode()).hasDefined(UUID)); List<OperationResponse.StreamEntry> streams = response.getInputStreams(); Assert.assertThat(streams, is(notNullValue())); Assert.assertThat(streams.size(), is(1)); try (InputStream in = streams.get(0).getStream()) { Properties content = new Properties(); content.load(in); Assert.assertThat(content.getProperty("service"), is(expectedValue)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } } @Override public void undeploy() { Future<?> future = manager.execute(manager.newDeploymentPlan() .undeploy("test-deployment.jar") .remove("test-deployment.jar") .build()); awaitDeploymentExecution(future); } @Override public void checkNoContent(String path) throws IOException { ModelNode op = new ModelNode(); op.get(OP).set(ClientConstants.READ_CONTENT_OPERATION); op.get(OP_ADDR).add(DEPLOYMENT, "test-deployment.jar"); op.get(PATH).set(path); Future<OperationResponse> future = client.executeOperationAsync(OperationBuilder.create(op, false).build(), null); try { OperationResponse response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); Assert.assertFalse(Operations.isSuccessfulOutcome(response.getResponseNode())); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } } @Override public void browseContent(String path, List<String> expectedContents) throws IOException { ModelNode op = new ModelNode(); op.get(OP).set(ClientConstants.DEPLOYMENT_BROWSE_CONTENT_OPERATION); op.get(OP_ADDR).add(DEPLOYMENT, "test-deployment.jar"); if (path != null && !path.isEmpty()) { op.get(PATH).set(path); } Future<ModelNode> future = client.executeAsync(OperationBuilder.create(op, false).build(), null); try { ModelNode response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); Assert.assertTrue(Operations.isSuccessfulOutcome(response)); List<ModelNode> contents = Operations.readResult(response).asList(); for (ModelNode content : contents) { Assert.assertTrue(content.hasDefined("path")); String contentPath = content.get("path").asString(); Assert.assertTrue(content.asString() + " isn't expected", expectedContents.contains(contentPath)); Assert.assertTrue(content.hasDefined("directory")); if (!content.get("directory").asBoolean()) { Assert.assertTrue(content.hasDefined("file-size")); } expectedContents.remove(contentPath); } Assert.assertTrue(expectedContents.isEmpty()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } } }); } private void testDeployments(boolean fromFile, ExplodedDeploymentExecutor deploymentExecutor) throws Exception { // Initial deploy Set<String> initialHashes = null; if (!fromFile) { initialHashes = getAllDeploymentHashesFromContentDir(true); } deploymentExecutor.initialDeploy(); //listener.await(); ServiceActivatorDeploymentUtil.validateProperties(managementClient.getControllerClient(), properties); String initialDeploymentHash = null; if (!fromFile) { Set<String> currentHashes = getAllDeploymentHashesFromContentDir(false); currentHashes.removeAll(initialHashes); Assert.assertEquals(1, currentHashes.size()); initialDeploymentHash = currentHashes.iterator().next(); } try { // Add content // listener.reset(2); deploymentExecutor.addContent(); deploymentExecutor.readContent("SimpleTest.properties", "is added"); deploymentExecutor.browseContent("", new ArrayList<>(Arrays.asList("META-INF/", "META-INF/MANIFEST.MF", "META-INF/services/", "META-INF/services/org.jboss.msc.service.ServiceActivator", "org/","org/jboss/","org/jboss/as/", "org/jboss/as/test/", "org/jboss/as/test/deployment/", "org/jboss/as/test/deployment/trivial/", "service-activator-deployment.properties", "org/jboss/as/test/deployment/trivial/ServiceActivatorDeployment.class", "SimpleTest.properties"))); if (!fromFile) { Set<String> currentHashes = getAllDeploymentHashesFromContentDir(false); Assert.assertFalse(currentHashes.contains(initialDeploymentHash)); //Should have been deleted when added currentHashes.removeAll(initialHashes); Assert.assertEquals(1, currentHashes.size()); } // listener.await(); deploymentExecutor.replaceContent(); ServiceActivatorDeploymentUtil.validateProperties(managementClient.getControllerClient(), properties3); deploymentExecutor.readContent("service-activator-deployment.properties", "is replaced"); if (!fromFile) { Set<String> currentHashes = getAllDeploymentHashesFromContentDir(false); Assert.assertFalse(currentHashes.contains(initialDeploymentHash)); //Should have been deleted when replaced currentHashes.removeAll(initialHashes); Assert.assertEquals(1, currentHashes.size()); } deploymentExecutor.removeContent(); deploymentExecutor.checkNoContent("SimpleTest.properties"); // Undeploy // listener.reset(1); } finally { deploymentExecutor.undeploy(); } if (!fromFile) { Assert.assertEquals(initialHashes, getAllDeploymentHashesFromContentDir(false)); } } private Set<String> getAllDeploymentHashesFromContentDir(boolean emptyOk) { String jbossBaseDir = System.getProperty("jboss.home"); Assert.assertNotNull(jbossBaseDir); File file = new File(jbossBaseDir); Assert.assertTrue(file.exists()); file = new File(file, "standalone"); Assert.assertTrue(file.exists()); file = new File(file, "data"); if (!file.exists() && emptyOk) { return new HashSet<>(); } Assert.assertTrue(file.exists()); file = new File(file, "content"); Assert.assertTrue(file.exists()); Set<String> hashes = new HashSet<>(); for (File top : file.listFiles()) { if (top.isDirectory() && top.getName().length() == 2) { for (File content : top.listFiles()) { hashes.add(top.getName() + content.getName()); } } } return hashes; } private void awaitDeploymentExecution(Future<?> future) { Object t = null; try { t = future.get(TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } } private interface ExplodedDeploymentExecutor { void initialDeploy() throws IOException; void addContent() throws IOException; void replaceContent() throws IOException; void removeContent() throws IOException; void readContent(String path, String expectedValue) throws IOException; void browseContent(String path, List<String> expectedContents) throws IOException; void checkNoContent(String path) throws IOException; void undeploy() throws IOException; } }