/*
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.wildfly.core.testrunner;
import static org.jboss.as.controller.client.helpers.ClientConstants.CONTROLLER_PROCESS_STATE_STARTING;
import static org.jboss.as.controller.client.helpers.ClientConstants.CONTROLLER_PROCESS_STATE_STOPPING;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT;
import static org.jboss.as.controller.client.helpers.ClientConstants.FAILURE_DESCRIPTION;
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.client.helpers.ClientConstants.OUTCOME;
import static org.jboss.as.controller.client.helpers.ClientConstants.READ_ATTRIBUTE_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.READ_RESOURCE_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.RECURSIVE;
import static org.jboss.as.controller.client.helpers.ClientConstants.RESULT;
import static org.jboss.as.controller.client.helpers.ClientConstants.SUCCESS;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import javax.management.remote.JMXServiceURL;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.network.NetworkUtils;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.logging.Logger;
/**
* A helper class to join management related operations, like extract sub system ip/port (web/jmx)
* and deployment introspection.
*
* @author <a href="aslak@redhat.com">Aslak Knutsen</a>
*/
public class ManagementClient implements AutoCloseable, Closeable {
private static final Logger logger = Logger.getLogger(ManagementClient.class);
private static final String UNDERTOW = "undertow";
private static final String NAME = "name";
private static final String POSTFIX_WEB = ".war";
private static final String POSTFIX_EAR = ".ear";
private final String mgmtAddress;
private final int mgmtPort;
private final String mgmtProtocol;
private final ModelControllerClient client;
private URI webUri;
private URI ejbUri;
// cache static RootNode
private ModelNode rootNode = null;
public ManagementClient(ModelControllerClient client, final String mgmtAddress, final int managementPort, final String protocol) {
if (client == null) {
throw new IllegalArgumentException("Client must be specified");
}
this.client = client;
this.mgmtAddress = mgmtAddress;
this.mgmtPort = managementPort;
this.mgmtProtocol = protocol;
}
//-------------------------------------------------------------------------------------||
// Public API -------------------------------------------------------------------------||
//-------------------------------------------------------------------------------------||
public ModelControllerClient getControllerClient() {
return client;
}
/**
* @return The base URI or the web subsystem. Usually http://localhost:8080
* @deprecated check if it is even used anywhere
*/
@Deprecated
public URI getWebUri() {
if (webUri == null) {
try {
webUri = new URI("http://localhost:8080");
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
try {
if (rootNode == null) {
readRootNode();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
ModelNode undertowNode = rootNode.get("subsystem", UNDERTOW);
if (undertowNode.isDefined()) {
List<Property> vhosts = undertowNode.get("server").asPropertyList();
ModelNode socketBinding = new ModelNode();
if (!vhosts.isEmpty()) {//if empty no virtual hosts defined
socketBinding = vhosts.get(0).getValue().get("http-listener", "default").get("socket-binding");
}
if (socketBinding.isDefined()) {
webUri = getBinding("http", socketBinding.asString());
}
}
}
return webUri;
}
public boolean isServerInRunningState() {
try {
ModelNode op = new ModelNode();
op.get(OP).set(READ_ATTRIBUTE_OPERATION);
op.get(OP_ADDR).setEmptyList();
op.get(NAME).set("server-state");
ModelNode rsp = client.execute(op);
return SUCCESS.equals(rsp.get(OUTCOME).asString())
&& !CONTROLLER_PROCESS_STATE_STARTING.equals(rsp.get(RESULT).asString())
&& !CONTROLLER_PROCESS_STATE_STOPPING.equals(rsp.get(RESULT).asString());
} catch (RuntimeException rte) {
throw rte;
} catch (IOException ex) {
return false;
}
}
@Override
public void close() {
try {
getControllerClient().close();
} catch (IOException e) {
throw new RuntimeException("Could not close connection", e);
}
}
private void readRootNode() throws Exception {
rootNode = readResource(new ModelNode());
}
private static ModelNode defined(final ModelNode node, final String message) {
if (!node.isDefined()) { throw new IllegalStateException(message); }
return node;
}
private URI getBinding(final String protocol, final String socketBinding) {
try {
final String socketBindingGroupName = rootNode.get("socket-binding-group").keys().iterator().next();
final ModelNode operation = new ModelNode();
operation.get(OP_ADDR).get("socket-binding-group").set(socketBindingGroupName);
operation.get(OP_ADDR).get("socket-binding").set(socketBinding);
operation.get(OP).set(READ_RESOURCE_OPERATION);
operation.get("include-runtime").set(true);
ModelNode binding = executeForResult(operation);
String ip = binding.get("bound-address").asString();
//it appears some system can return a binding with the zone specifier on the end
if (ip.contains(":") && ip.contains("%")) {
ip = ip.split("%")[0];
}
final int port = defined(binding.get("bound-port"), socketBindingGroupName + " -> " + socketBinding + " -> bound-port is undefined").asInt();
return URI.create(protocol + "://" + NetworkUtils.formatPossibleIpv6Address(ip) + ":" + port);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//-------------------------------------------------------------------------------------||
// Metadata Extraction Operations -----------------------------------------------------||
//-------------------------------------------------------------------------------------||
private boolean isEnterpriseArchive(String deploymentName) {
return deploymentName.endsWith(POSTFIX_EAR);
}
private boolean isWebArchive(String deploymentName) {
return deploymentName.endsWith(POSTFIX_WEB);
}
private ModelNode createDeploymentAddress(String deploymentName) {
ModelNode address = new ModelNode();
address.add(DEPLOYMENT, deploymentName);
return address;
}
private String toContextName(String deploymentName) {
String correctedName = deploymentName;
if (correctedName.startsWith("/")) {
correctedName = correctedName.substring(1);
}
return correctedName;
}
//-------------------------------------------------------------------------------------||
// Common Management API Operations ---------------------------------------------------||
//-------------------------------------------------------------------------------------||
private ModelNode readResource(ModelNode address) throws Exception {
final ModelNode operation = new ModelNode();
operation.get(OP).set(READ_RESOURCE_OPERATION);
operation.get(RECURSIVE).set("true");
operation.get(OP_ADDR).set(address);
return executeForResult(operation);
}
/**
* Executes an operation and returns the result. If the operation was not successful an
* {@link UnsuccessfulOperationException}
* will be thrown.
*
* @param operation The operation
* @return The result
* @throws UnsuccessfulOperationException if the operation failed
*/
public ModelNode executeForResult(final ModelNode operation) throws UnsuccessfulOperationException {
try {
final ModelNode result = client.execute(operation);
checkSuccessful(result, operation);
return result.get(RESULT);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void checkSuccessful(final ModelNode result,
final ModelNode operation) throws UnsuccessfulOperationException {
if (!SUCCESS.equals(result.get(OUTCOME).asString())) {
logger.error("Operation " + operation + " did not succeed. Result was " + result);
throw new UnsuccessfulOperationException(result.get(
FAILURE_DESCRIPTION).toString());
}
}
public JMXServiceURL getRemoteJMXURL() {
try {
switch (mgmtProtocol) {
case "http-remoting":
case "remote+http":
return new JMXServiceURL("service:jmx:remote+http://" + NetworkUtils.formatPossibleIpv6Address(mgmtAddress) + ":" + mgmtPort);
case "https-remoting":
case "remote+https":
return new JMXServiceURL("service:jmx:remote+https://" + NetworkUtils.formatPossibleIpv6Address(mgmtAddress) + ":" + mgmtPort);
default:
return new JMXServiceURL("service:jmx:remote://" + NetworkUtils.formatPossibleIpv6Address(mgmtAddress) + ":" + mgmtPort);
}
} catch (Exception e) {
throw new RuntimeException("Could not create JMXServiceURL:" + this, e);
}
}
public int getMgmtPort() {
return mgmtPort;
}
public String getMgmtAddress() {
return NetworkUtils.formatPossibleIpv6Address(mgmtAddress);
}
public String getMgmtProtocol() {
return mgmtProtocol;
}
/**
* @deprecated check if it is even used anywhere
*/
@Deprecated
public URI getRemoteEjbURL() {
if (ejbUri == null) {
URI webUri = getWebUri();
try {
ejbUri = new URI("remote+http", webUri.getUserInfo(), webUri.getHost(), webUri.getPort(),null,null,null);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
return ejbUri;
}
}