/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat Middleware LLC, 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.integration.domain.mixed;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD_CONTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.COMPOSITE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT;
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.EMPTY;
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.REPLACE_DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_GROUP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.TARGET_PATH;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.TO_REPLACE;
import static org.jboss.as.controller.operations.common.Util.createAddOperation;
import static org.jboss.as.controller.operations.common.Util.createEmptyOperation;
import static org.jboss.as.controller.operations.common.Util.createRemoveOperation;
import static org.jboss.as.test.integration.domain.management.util.DomainTestSupport.validateResponse;
import static org.jboss.as.test.integration.domain.management.util.DomainTestSupport.validateFailedResponse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.xnio.IoUtils.safeClose;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.List;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.client.Operation;
import org.jboss.as.controller.client.OperationBuilder;
import org.jboss.as.test.integration.domain.management.util.DomainTestSupport;
import org.jboss.as.test.integration.domain.mixed.jsf.Bean;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.dmr.ModelNode;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.exporter.ExplodedExporter;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
*/
public abstract class MixedDomainDeploymentTest {
private static final String TEST = "test.war";
private static final String REPLACEMENT = "test.war.v2";
private static final PathAddress ROOT_DEPLOYMENT_ADDRESS = PathAddress.pathAddress(DEPLOYMENT, TEST);
private static final PathAddress ROOT_REPLACEMENT_ADDRESS = PathAddress.pathAddress(DEPLOYMENT, REPLACEMENT);
private static final PathAddress OTHER_SERVER_GROUP_ADDRESS =
PathAddress.pathAddress(SERVER_GROUP, "other-server-group");
private static final PathAddress OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS =
OTHER_SERVER_GROUP_ADDRESS.append(DEPLOYMENT, TEST);
private static final PathAddress MAIN_SERVER_GROUP_ADDRESS =
PathAddress.pathAddress(SERVER_GROUP, "main-server-group");
private static final PathAddress MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS =
MAIN_SERVER_GROUP_ADDRESS.append(DEPLOYMENT, TEST);
private WebArchive webArchive;
private WebArchive webArchive2;
private WebArchive jsfTestArchive;
private MixedDomainTestSupport testSupport;
private File tmpDir;
@Before
public void setupDomain() throws Exception {
// Create our deployments
webArchive = ShrinkWrap.create(WebArchive.class, TEST);
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
URL index = tccl.getResource("helloWorld/index.html");
webArchive.addAsWebResource(index, "index.html");
webArchive2 = ShrinkWrap.create(WebArchive.class, TEST);
index = tccl.getResource("helloWorld/index.html");
webArchive2.addAsWebResource(index, "index.html");
index = tccl.getResource("helloWorld/index2.html");
webArchive2.addAsWebResource(index, "index2.html");
// Make versions on the filesystem for URL-based deploy and for unmanaged content testing
tmpDir = new File("target/deployments/" + this.getClass().getSimpleName());
new File(tmpDir, "archives").mkdirs();
new File(tmpDir, "exploded").mkdirs();
webArchive.as(ZipExporter.class).exportTo(new File(tmpDir, "archives/" + TEST), true);
webArchive.as(ExplodedExporter.class).exportExploded(new File(tmpDir, "exploded"));
//Make the jsf war to test that jsf works on the older slaves that did not have the jsf subsystem
jsfTestArchive = ShrinkWrap.create(WebArchive.class, "jsf-test.war");
jsfTestArchive.addClass(Bean.class);
jsfTestArchive.addAsWebResource("jsf-test/index.html");
jsfTestArchive.addAsWebResource("jsf-test/home.xhtml");
jsfTestArchive.addAsWebInfResource("jsf-test/WEB-INF/beans.xml");
jsfTestArchive.addAsWebInfResource("jsf-test/WEB-INF/faces-config.xml");
// Launch the domain
testSupport = MixedDomainTestSuite.getSupport(this.getClass());
confirmNoDeployments();
}
@AfterClass
public static synchronized void afterClass() {
MixedDomainTestSuite.afterClass();
}
/**
* Validate that there are no deployments; try and clean if there are.
*
* @throws Exception
*/
@After
public void confirmNoDeployments() throws Exception {
List<ModelNode> deploymentList = getDeploymentList(PathAddress.EMPTY_ADDRESS);
if (deploymentList.size() > 0) {
cleanDeployments();
}
deploymentList = getDeploymentList(PathAddress.EMPTY_ADDRESS);
assertEquals("Deployments are removed from the domain", 0, deploymentList.size());
try {
performHttpCall(DomainTestSupport.masterAddress, 8080);
fail(TEST + " is available on main-one");
} catch (IOException good) {
// good
}
try {
performHttpCall(DomainTestSupport.slaveAddress, 8630);
fail(TEST + " is available on other-three");
} catch (IOException good) {
// good
}
}
@Test
public void testDeploymentViaUrl() throws Exception {
String url = new File(tmpDir, "archives/" + TEST).toURI().toURL().toString();
ModelNode content = new ModelNode();
content.get("url").set(url);
ModelNode composite = createDeploymentOperation(content, OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS, MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS);
executeOnMaster(composite);
performHttpCall(DomainTestSupport.slaveAddress, 8080);
}
@Test
public void testDeploymentViaStream() throws Exception {
ModelNode content = new ModelNode();
content.get(INPUT_STREAM_INDEX).set(0);
ModelNode composite = createDeploymentOperation(content, OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS, MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS);
OperationBuilder builder = new OperationBuilder(composite, true);
builder.addInputStream(webArchive.as(ZipExporter.class).exportAsInputStream());
executeOnMaster(builder.build());
performHttpCall(DomainTestSupport.slaveAddress, 8080);
}
@Test
public void testUnmanagedArchiveDeployment() throws Exception {
ModelNode content = new ModelNode();
content.get("archive").set(true);
content.get("path").set(new File(tmpDir, "archives/" + TEST).getAbsolutePath());
ModelNode composite = createDeploymentOperation(content, OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS, MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS);
executeOnMaster(composite);
performHttpCall(DomainTestSupport.slaveAddress, 8080);
}
@Test
public void testUnmanagedExplodedDeployment() throws Exception {
ModelNode content = new ModelNode();
content.get("archive").set(false);
content.get("path").set(new File(tmpDir, "exploded/" + TEST).getAbsolutePath());
ModelNode composite = createDeploymentOperation(content, OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS, MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS);
executeOnMaster(composite);
performHttpCall(DomainTestSupport.slaveAddress, 8080);
}
protected boolean supportManagedExplodedDeployment() {
return true;
}
@Test
public void testExplodedEmptyDeployment() throws Exception {
ModelNode empty = new ModelNode();
empty.get(EMPTY).set(true);
ModelNode composite = createDeploymentOperation(empty, OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS, MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS);
if (supportManagedExplodedDeployment()) {
executeOnMaster(composite);
} else {
ModelNode failure = executeForFailureOnMaster(composite);
assertTrue(failure.toJSONString(true), failure.toJSONString(true).contains("WFLYCTL0421:"));
}
}
@Test
public void testExplodedDeployment() throws Exception {
ModelNode composite = createEmptyOperation(COMPOSITE, PathAddress.EMPTY_ADDRESS);
ModelNode steps = composite.get(STEPS);
ModelNode op = createAddOperation(ROOT_DEPLOYMENT_ADDRESS);
op.get(CONTENT).setEmptyList();
ModelNode empty = new ModelNode();
empty.get(EMPTY).set(true);
op.get(CONTENT).add(empty);
steps.add(op);
op = createEmptyOperation(ADD_CONTENT, ROOT_DEPLOYMENT_ADDRESS);
op.get(CONTENT).setEmptyList();
ModelNode file = new ModelNode();
file.get(TARGET_PATH).set("index.html");
file.get(INPUT_STREAM_INDEX).set(0);
op.get(CONTENT).add(file);
file = new ModelNode();
file.get(TARGET_PATH).set("index2.html");
file.get(INPUT_STREAM_INDEX).set(1);
op.get(CONTENT).add(file);
steps.add(op);
ModelNode sg = steps.add();
sg.set(createAddOperation(OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS));
sg.get(ENABLED).set(true);
sg = steps.add();
sg.set(createAddOperation(MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS));
sg.get(ENABLED).set(true);
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
OperationBuilder builder = new OperationBuilder(composite);
builder.addInputStream(tccl.getResourceAsStream("helloWorld/index.html"));
builder.addInputStream(tccl.getResourceAsStream("helloWorld/index2.html"));
if (supportManagedExplodedDeployment()) {
executeOnMaster(builder.build());
performHttpCall(DomainTestSupport.slaveAddress, 8080);
} else {
ModelNode failure = executeForFailureOnMaster(builder.build());
assertTrue(failure.toJSONString(true), failure.toJSONString(true).contains("WFLYCTL0421:"));
}
}
@Test
public void testUndeploy() throws Exception {
// Establish the deployment
testDeploymentViaStream();
undeployTest();
}
@Test
public void testUnmanagedArchiveUndeploy() throws Exception {
// Establish the deployment
testUnmanagedArchiveDeployment();
undeployTest();
}
@Test
public void testUnmanagedExplodedUndeploy() throws Exception {
// Establish the deployment
testUnmanagedExplodedDeployment();
undeployTest();
}
@Test
public void testRedeploy() throws Exception {
// Establish the deployment
testDeploymentViaStream();
redeployTest();
}
@Test
public void testUnmanagedArchiveRedeploy() throws Exception {
// Establish the deployment
testUnmanagedArchiveDeployment();
redeployTest();
}
@Test
public void testUnmanagedExplodedRedeploy() throws Exception {
// Establish the deployment
testUnmanagedExplodedDeployment();
redeployTest();
}
@Test
public void testReplace() throws Exception {
// Establish the deployment
testDeploymentViaStream();
ModelNode content = new ModelNode();
content.get(INPUT_STREAM_INDEX).set(0);
ModelNode op = createDeploymentReplaceOperation(content, OTHER_SERVER_GROUP_ADDRESS, MAIN_SERVER_GROUP_ADDRESS);
OperationBuilder builder = new OperationBuilder(op, true);
builder.addInputStream(webArchive.as(ZipExporter.class).exportAsInputStream());
executeOnMaster(builder.build());
performHttpCall(DomainTestSupport.slaveAddress, 8080);
}
@Test
public void testJsfWorks() throws Exception {
ModelNode content = new ModelNode();
content.get(INPUT_STREAM_INDEX).set(0);
//Just be lazy here and deploy the jsf-test.war with the same name as the other deployments we tried
ModelNode composite = createDeploymentOperation(content, OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS, MAIN_SERVER_GROUP_DEPLOYMENT_ADDRESS);
OperationBuilder builder = new OperationBuilder(composite, true);
builder.addInputStream(jsfTestArchive.as(ZipExporter.class).exportAsInputStream());
executeOnMaster(builder.build());
performHttpCall(DomainTestSupport.slaveAddress, 8080, "test/home.jsf", "Bean Works");
}
private void redeployTest() throws IOException {
ModelNode op = createEmptyOperation("redeploy", OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS);
executeOnMaster(op);
performHttpCall(DomainTestSupport.slaveAddress, 8080);
}
private void undeployTest() throws Exception {
ModelNode op = createEmptyOperation("undeploy", OTHER_SERVER_GROUP_DEPLOYMENT_ADDRESS);
executeOnMaster(op);
// Thread.sleep(1000);
try {
performHttpCall(DomainTestSupport.slaveAddress, 8080);
fail("Webapp still accessible following undeploy");
} catch (IOException good) {
// desired result
}
}
/**
* Remove all deployments from the model.
*
* @throws IOException
*/
private void cleanDeployments() throws IOException {
List<ModelNode> deploymentList = getDeploymentList(OTHER_SERVER_GROUP_ADDRESS);
for (ModelNode deployment : deploymentList) {
removeDeployment(deployment.asString(), OTHER_SERVER_GROUP_ADDRESS);
}
deploymentList = getDeploymentList(MAIN_SERVER_GROUP_ADDRESS);
for (ModelNode deployment : deploymentList) {
removeDeployment(deployment.asString(), MAIN_SERVER_GROUP_ADDRESS);
}
deploymentList = getDeploymentList(PathAddress.EMPTY_ADDRESS);
for (ModelNode deployment : deploymentList) {
removeDeployment(deployment.asString(), PathAddress.EMPTY_ADDRESS);
}
}
private ModelNode executeOnMaster(ModelNode op) throws IOException {
return validateResponse(testSupport.getDomainMasterLifecycleUtil().getDomainClient().execute(op));
}
private ModelNode executeOnMaster(Operation op) throws IOException {
return validateResponse(testSupport.getDomainMasterLifecycleUtil().getDomainClient().execute(op));
}
private ModelNode executeForFailureOnMaster(ModelNode op) throws IOException {
return validateFailedResponse(testSupport.getDomainMasterLifecycleUtil().getDomainClient().execute(op));
}
private ModelNode executeForFailureOnMaster(Operation op) throws IOException {
return validateFailedResponse(testSupport.getDomainMasterLifecycleUtil().getDomainClient().execute(op));
}
private ModelNode createDeploymentOperation(ModelNode content, PathAddress... serverGroupAddressses) {
ModelNode composite = createEmptyOperation(COMPOSITE, PathAddress.EMPTY_ADDRESS);
ModelNode steps = composite.get(STEPS);
ModelNode step1 = steps.add();
step1.set(createAddOperation(ROOT_DEPLOYMENT_ADDRESS));
step1.get(CONTENT).add(content);
for (PathAddress serverGroup : serverGroupAddressses) {
ModelNode sg = steps.add();
sg.set(createAddOperation(serverGroup));
sg.get(ENABLED).set(true);
}
return composite;
}
private ModelNode createDeploymentReplaceOperation(ModelNode content, PathAddress... serverGroupAddressses) {
ModelNode composite = createEmptyOperation(COMPOSITE, PathAddress.EMPTY_ADDRESS);
ModelNode steps = composite.get(STEPS);
ModelNode step1 = steps.add();
step1.set(createAddOperation(ROOT_REPLACEMENT_ADDRESS));
step1.get(RUNTIME_NAME).set(TEST);
step1.get(CONTENT).add(content);
for (PathAddress serverGroup : serverGroupAddressses) {
ModelNode sgr = steps.add();
sgr.set(createEmptyOperation(REPLACE_DEPLOYMENT, serverGroup));
sgr.get(ENABLED).set(true);
sgr.get(NAME).set(REPLACEMENT);
sgr.get(TO_REPLACE).set(TEST);
}
return composite;
}
private List<ModelNode> getDeploymentList(PathAddress address) throws IOException {
ModelNode op = createEmptyOperation("read-children-names", address);
op.get("child-type").set("deployment");
ModelNode response = testSupport.getDomainMasterLifecycleUtil().getDomainClient().execute(op);
ModelNode result = validateResponse(response);
return result.isDefined() ? result.asList() : Collections.<ModelNode>emptyList();
}
private void performHttpCall(String host, int port) throws IOException {
performHttpCall(host, port, "test/index.html", "Hello World");
}
private void performHttpCall(String host, int port, String path, String expected) throws IOException {
URLConnection conn = null;
InputStream in = null;
StringWriter writer = new StringWriter();
try {
URL url = new URL("http://" + TestSuiteEnvironment.formatPossibleIpv6Address(host) + ":" + port + "/" + path);
//System.out.println("Reading response from " + url + ":");
conn = url.openConnection();
conn.setDoInput(true);
in = new BufferedInputStream(conn.getInputStream());
int i = in.read();
while (i != -1) {
writer.write((char) i);
i = in.read();
}
assertTrue((writer.toString().indexOf(expected) > -1));
//System.out.println("OK");
} finally {
safeClose(in);
safeClose(writer);
}
}
private void removeDeployment(String deploymentName, PathAddress address) throws IOException {
ModelNode op = createRemoveOperation(address.append(DEPLOYMENT, deploymentName));
ModelNode response = testSupport.getDomainMasterLifecycleUtil().getDomainClient().execute(op);
validateResponse(response);
}
}