/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.jbossas5.test.util;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.Set;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.jboss.deployers.spi.management.deploy.DeploymentManager;
import org.jboss.deployers.spi.management.deploy.DeploymentProgress;
import org.jboss.deployers.spi.management.deploy.DeploymentStatus;
import org.rhq.core.clientapi.agent.metadata.PluginMetadataManager;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.inventory.InventoryManager;
import org.rhq.core.pc.inventory.ResourceContainer;
import org.rhq.core.pc.util.ComponentUtil;
import org.rhq.core.pc.util.FacetLockType;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.plugins.jbossas5.ProfileServiceComponent;
/**
* Various application server related utility methods used by the tests.
*
* @author Lukas Krejci
*/
public class AppServerUtils {
public static final String PLUGIN_NAME = "JBossAS5";
public static final String APPLICATION_SERVER_RESOURCE_NAME = "JBossAS Server";
public static final long DEFAULT_TIMEOUT = 30000; //30s
public static final long RESTART_TIMEOUT = 300000; //5 minutes
private AppServerUtils() {
}
/**
* Deploys an archive to the AS and starts it.
*
* @param name the name of the archive
* @param archiveFile the archive file
* @param exploded whether to deploy exploded or not
* @throws Exception
*/
public static void deployFileToAS(String name, File archiveFile, boolean exploded) throws Exception {
DeploymentManager deploymentManager = getDeploymentManager();
DeploymentProgress progress = deploymentManager.distribute(name, archiveFile.toURI().toURL(), exploded);
progress.run();
DeploymentStatus status = progress.getDeploymentStatus();
if (status.isFailed()) {
throw new IllegalStateException("Failed to distribute " + archiveFile.getAbsolutePath() + " with message: "
+ status.getMessage());
}
String[] deploymentNames = progress.getDeploymentID().getRepositoryNames();
progress = deploymentManager.start(deploymentNames);
progress.run();
status = progress.getDeploymentStatus();
if (status.isFailed()) {
throw new IllegalStateException("Failed to start " + archiveFile.getAbsolutePath() + " with message: "
+ status.getMessage());
}
}
/**
* Undeploys an archive of given name from the AS
*
* @param archiveName
* @throws Exception
*/
public static void undeployFromAS(String archiveName) throws Exception {
DeploymentManager deploymentManager = getDeploymentManager();
DeploymentProgress progress = deploymentManager.stop(archiveName);
progress.run();
DeploymentStatus status = progress.getDeploymentStatus();
if (status.isFailed()) {
throw new IllegalStateException("Failed to stop " + archiveName + " with message: "
+ status.getMessage());
}
progress = deploymentManager.remove(archiveName);
progress.run();
status = progress.getDeploymentStatus();
if (status.isFailed()) {
throw new IllegalStateException("Failed to undeploy " + archiveName + " with message: "
+ status.getMessage());
}
}
/**
* @return The application server resource from the plugin container's inventory.
*
* @throws IllegalStateException if there is none or more than 1 app servers
* in the plugin container's inventory.
*/
public static Resource getASResource() {
PluginMetadataManager pluginMetadataManager = PluginContainer.getInstance().getPluginManager()
.getMetadataManager();
ResourceType appServerResourceType = pluginMetadataManager.getType(APPLICATION_SERVER_RESOURCE_NAME,
PLUGIN_NAME);
InventoryManager inventoryManager = PluginContainer.getInstance().getInventoryManager();
Set<Resource> appServers = inventoryManager.getResourcesWithType(appServerResourceType);
if (appServers.size() != 1) {
throw new IllegalStateException("Expected to find exactly 1 AS5 server, but found " + appServers.size()
+ " instead.");
}
return appServers.iterator().next();
}
/**
* Returns a proxy of given interface for given resource.
* <p>
* The proxy is created using the resource's classloader and therefore cannot be directly
* cast to the facetInterface, which is most probably loaded in a different classloader.
* <p>
* The return type is therefore an object and you have to use reflection to work with it.
*
* @param <T>
* @param resource
* @param facetInterface
* @return
* @throws Exception
*/
public static <T> Object getResourceProxy(Resource resource, Class<T> facetInterface, long timeout) throws Exception {
ClassLoader currenContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
ClassLoader cl = PluginContainer.getInstance().getPluginComponentFactory().getResourceClassloader(
resource);
Class<?> resourceSpecificFacetInterface = Class.forName(facetInterface.getName(), true, cl);
//use the resource specific classloader for the proxy creation
Thread.currentThread().setContextClassLoader(cl);
return ComponentUtil.getComponent(resource.getId(), resourceSpecificFacetInterface, FacetLockType.WRITE,
timeout, true, true, true);
} finally {
Thread.currentThread().setContextClassLoader(currenContextClassLoader);
}
}
public static <T> Object getASComponentProxy(Class<T> facetInterface) throws Exception {
return getResourceProxy(getASResource(), facetInterface, DEFAULT_TIMEOUT);
}
public static <T> T getRemoteObject(String jndiName, Class<T> clazz) throws Exception {
InitialContext initialContext = getAppServerInitialContext();
return clazz.cast(initialContext.lookup(jndiName));
}
public static InitialContext getAppServerInitialContext() throws NamingException {
ResourceContainer resourceContainer = PluginContainer.getInstance().getInventoryManager().getResourceContainer(
getASResource());
Configuration asConfiguration = resourceContainer.getResourceContext().getPluginConfiguration();
Properties env = new Properties();
env.setProperty(Context.PROVIDER_URL, asConfiguration.getSimpleValue("namingURL", null));
env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
env.setProperty("jnp.timeout", "60000");
env.setProperty("jnp.sotimeout", "60000");
env.setProperty("jnp.disableDiscovery", "true");
return new InitialContext(env);
}
/**
* A helper method to invoke a method on an object using reflection.
*
* @param methodName the name of the method to invoke
* @param instance the instance to invoke the method upon
* @param methodArgTypesAndValues the method argument types and values
* @return the result of the method call
* @throws Exception on error
*/
public static Object invokeMethod(String methodName, Object instance, MethodArgDef... methodArgTypesAndValues)
throws Exception {
Class<?>[] argTypes = null;
Object[] argValues = null;
if (methodArgTypesAndValues != null) {
argTypes = new Class<?>[methodArgTypesAndValues.length];
argValues = new Object[methodArgTypesAndValues.length];
for (int i = 0; i < methodArgTypesAndValues.length; ++i) {
argTypes[i] = methodArgTypesAndValues[i].getType();
argValues[i] = methodArgTypesAndValues[i].getValue();
}
}
Method method = instance.getClass().getMethod(methodName, argTypes);
return method.invoke(instance, argValues);
}
public static void shutdownServer() throws Exception {
System.out.println("Shutting down the server.");
Object asComponent = getResourceProxy(getASResource(), OperationFacet.class, RESTART_TIMEOUT);
long now = System.currentTimeMillis();
OperationResult result = (OperationResult) invokeMethod("invokeOperation", asComponent,
new MethodArgDef(String.class, "shutdown"),
new MethodArgDef(Configuration.class, new Configuration()));
if (result.getErrorMessage() != null) {
throw new Exception("Shutting down the server failed.");
}
System.out.println("The shutdown operation finished in " + ((System.currentTimeMillis() - now) / 1000D) + " seconds.");
}
public static void startServer() throws Exception {
System.out.println("Starting the server.");
Object asComponent = getResourceProxy(getASResource(), OperationFacet.class, RESTART_TIMEOUT);
long now = System.currentTimeMillis();
OperationResult result = (OperationResult) invokeMethod("invokeOperation", asComponent,
new MethodArgDef(String.class, "start"),
new MethodArgDef(Configuration.class, new Configuration()));
if (result.getErrorMessage() != null) {
throw new Exception("Starting the server failed.");
}
System.out.println("The start operation finished. Let's see if the plugin noticed the server back up...");
waitForServerUp();
long diff = System.currentTimeMillis() - now;
System.out.println("Server up in " + (diff / 1000D) + " seconds.");
System.out.println("Issuing availability scan after the server start...");
PluginContainer.getInstance().getInventoryManager().executeAvailabilityScanImmediately(false);
System.out.println("Server start procedure completed.");
}
public static void restartServer() throws Exception {
System.out.println("Restarting the server.");
Object asComponent = getResourceProxy(getASResource(), OperationFacet.class, RESTART_TIMEOUT);
long now = System.currentTimeMillis();
OperationResult result = (OperationResult) invokeMethod("invokeOperation", asComponent,
new MethodArgDef(String.class, "restart"),
new MethodArgDef(Configuration.class, new Configuration()));
if (result.getErrorMessage() != null) {
throw new Exception("Restart of the server failed.");
}
System.out.println("The restart operation finished. Let's see if the plugin noticed the server back up...");
waitForServerUp();
long diff = System.currentTimeMillis() - now;
System.out.println("Server back up in " + (diff / 1000D) + " seconds.");
System.out.println("Issuing availability and service scan after the server restart...");
PluginContainer.getInstance().getInventoryManager().executeAvailabilityScanImmediately(false);
PluginContainer.getInstance().getInventoryManager().executeServiceScanImmediately();
System.out.println("Server restart procedure completed.");
}
private static DeploymentManager getDeploymentManager() throws Exception {
Object asComponent = getASComponentProxy(ProfileServiceComponent.class);
Object connection = invokeMethod("getConnection", asComponent, (MethodArgDef[])null);
//I wonder why this cast works... where does the DeploymentManager get loaded from in plugin and in here?
return (DeploymentManager) invokeMethod("getDeploymentManager", connection, (MethodArgDef[])null);
}
private static void waitForServerUp() throws InterruptedException {
//wait until we see the server back up
InventoryManager inventoryManager = PluginContainer.getInstance().getInventoryManager();
Resource asResource = getASResource();
boolean serverUp = inventoryManager.getCurrentAvailability(asResource, false).forResource(asResource.getId()) == AvailabilityType.UP;
while(!serverUp) {
System.out.println("Waiting for the plugin to notice the server back up...");
Thread.sleep(1000);
serverUp = inventoryManager.getCurrentAvailability(asResource, false).forResource(asResource.getId()) == AvailabilityType.UP;
}
//cool, the component reports the server up, but we need to wait a bit more
//so that the server can finish up its start up procedures...
Thread.sleep(3000);
}
}