/*
* JBoss, Home of Professional Open Source
* Copyright 2008, 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.
*
* 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.test.classloading.vfs.client.support.launcher;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import org.jboss.beans.metadata.plugins.builder.BeanMetaDataBuilderFactory;
import org.jboss.beans.metadata.spi.BeanMetaData;
import org.jboss.beans.metadata.spi.BeanMetaDataFactory;
import org.jboss.beans.metadata.spi.ValueMetaData;
import org.jboss.beans.metadata.spi.builder.BeanMetaDataBuilder;
import org.jboss.classloader.plugins.ClassLoaderUtils;
import org.jboss.classloading.spi.metadata.ClassLoadingMetaDataFactory;
import org.jboss.classloading.spi.vfs.metadata.VFSClassLoaderFactory;
import org.jboss.dependency.spi.Controller;
import org.jboss.dependency.spi.ControllerMode;
import org.jboss.dependency.spi.ControllerState;
import org.jboss.kernel.Kernel;
import org.jboss.kernel.plugins.bootstrap.AbstractBootstrap;
import org.jboss.kernel.plugins.bootstrap.basic.BasicBootstrap;
import org.jboss.kernel.plugins.deployment.AbstractKernelDeployment;
import org.jboss.kernel.plugins.deployment.xml.BasicXMLDeployer;
import org.jboss.kernel.spi.dependency.KernelController;
import org.jboss.kernel.spi.dependency.KernelControllerContext;
import org.jboss.kernel.spi.deployment.KernelDeployment;
import org.jboss.logging.Logger;
/**
* An application client launcher mock up that uses the mc, vfs, class loaders
* to launch the application client environment and call its main method.
*
* @author Scott.Stark@jboss.org
* @version $Revision:$
*/
public class ClientLauncher
{
private static Logger log = Logger.getLogger(ClientLauncher.class);
private static Exception exception;
/** The kernel */
private static Kernel kernel;
/** The deployer */
private static BasicXMLDeployer deployer;
/** The default mode */
private static ControllerMode defaultMode = ControllerMode.AUTOMATIC;
private static void init() throws Throwable
{
// Bootstrap the kernel
AbstractBootstrap bootstrap = getBootstrap();
bootstrap.run();
kernel = bootstrap.getKernel();
// Create the deployer
deployer = createDeployer();
}
private static BasicXMLDeployer createDeployer()
{
return new BasicXMLDeployer(kernel, defaultMode);
}
/**
* Get the defaultMode.
*
* @return the defaultMode.
*/
public ControllerMode getDefaultMode()
{
return defaultMode;
}
/**
* Set the defaultMode.
*
* @param defaultMode the defaultMode.
*/
public void setDefaultMode(ControllerMode defaultMode)
{
this.defaultMode = defaultMode;
}
/**
* Get the kernel bootstrap
*
* @return the bootstrap
* @throws Exception for any error
*/
private static AbstractBootstrap getBootstrap() throws Exception
{
return new BasicBootstrap();
}
/**
* Get a bean
*
* @param name the name of the bean
* @param state the state of the bean
* @return the bean
* @throws IllegalStateException when the bean does not exist at that state
*/
private static Object getBean(final Object name, final ControllerState state)
{
KernelControllerContext context = getControllerContext(name, state);
return context.getTarget();
}
/**
* Get a bean
*
* @param <T> the expected type
* @param name the name of the bean
* @param state the state of the bean
* @param expected the expected type
* @return the bean
* @throws IllegalStateException when the bean does not exist at that state
*/
private static <T> T getBean(final Object name, final ControllerState state, final Class<T> expected)
{
if (expected == null)
throw new IllegalArgumentException("Null expected");
Object bean = getBean(name, state);
if (expected.isInstance(bean) == false)
throw new ClassCastException(ClassLoaderUtils.classToString(bean.getClass()) + " is not an instanceof " + ClassLoaderUtils.classToString(expected));
return expected.cast(bean);
}
/**
* Get a context
*
* @param name the name of the bean
* @param state the state of the bean
* @return the context
* @throws IllegalStateException when the context does not exist at that state
*/
private static KernelControllerContext getControllerContext(final Object name, final ControllerState state)
{
KernelController controller = kernel.getController();
KernelControllerContext context = (KernelControllerContext) controller.getContext(name, state);
if (context == null)
return handleNotFoundContext(controller, name, state);
return context;
}
/**
* Handle not found context.
*
* @param controller the controller
* @param name the name of the bean
* @param state the state of the bean
* @return the context
* @throws IllegalStateException when the context does not exist at that state
*/
private static KernelControllerContext handleNotFoundContext(Controller controller, Object name, ControllerState state)
{
throw new IllegalStateException("Bean not found: '" + name + "' at state " + state);
}
/**
* Validate
*
* @throws Exception for any error
*/
private static void validate() throws Exception
{
try
{
deployer.validate();
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw e;
}
catch (Error e)
{
throw e;
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
/**
* Deploy a url
*
* @param url the deployment url
* @return the deployment
* @throws Exception for any error
*/
private static KernelDeployment deploy(URL url) throws Exception
{
try
{
log.debug("Deploying " + url);
KernelDeployment deployment = deployer.deploy(url);
log.trace("Deployed " + url);
return deployment;
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw e;
}
catch (Error e)
{
throw e;
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
/**
* Deploy a deployment
*
* @param deployment the deployment
* @throws Exception for any error
*/
private static void deploy(KernelDeployment deployment) throws Exception
{
log.debug("Deploying " + deployment);
try
{
deployer.deploy(deployment);
log.debug("Deployed " + deployment);
}
catch (Exception e)
{
throw e;
}
catch (Error e)
{
throw e;
}
catch (Throwable t)
{
throw new RuntimeException("Error deploying deployment: " + deployment, t);
}
}
/**
* Undeploy a deployment
*
* @param deployment the deployment
*/
private static void undeploy(KernelDeployment deployment)
{
log.debug("Undeploying " + deployment.getName());
try
{
deployer.undeploy(deployment);
log.trace("Undeployed " + deployment.getName());
}
catch (Throwable t)
{
log.warn("Error during undeployment: " + deployment.getName(), t);
}
}
/**
* Undeploy a deployment
*
* @param url the url
*/
protected void undeploy(URL url)
{
log.debug("Undeploying " + url);
try
{
deployer.undeploy(url);
log.trace("Undeployed " + url);
}
catch (Throwable t)
{
log.warn("Error during undeployment: " + url, t);
}
}
@SuppressWarnings("unused")
private static void shutdown()
{
kernel.getController().shutdown();
}
/**
* Undeploy all
*/
@SuppressWarnings("unused")
private static void undeploy()
{
log.debug("Undeploying " + deployer.getDeploymentNames());
deployer.shutdown();
}
public static Exception getException()
{
return exception;
}
/**
* The client launcher entry point that create an mc to launch the client container.
* @param clientClass
* @param clientName
* @param cp
* @param args
* @throws Throwable
*/
@SuppressWarnings({"unchecked", "deprecation"})
public static void launch(String clientClass, String clientName, String[] cp, String[] args)
throws Throwable
{
// Init the kernel and deployers
init();
// Deploy the common launcher beans
String common = "/org/jboss/test/classloading/vfs/metadata/Common.xml";
URL url = ClientLauncher.class.getResource(common);
if (url == null)
throw new IllegalStateException(common + " not found");
deploy(url);
validate();
try
{
VFSClassLoaderFactory factory = new VFSClassLoaderFactory("ClientLauncherClassLoader");
ArrayList<String> roots = new ArrayList<String>();
// This will come from the client metadata
for(String path : cp)
roots.add(path);
factory.setRoots(roots);
// Do we have to export all packages? Not going to know them...
int lastDot = clientClass.lastIndexOf('.');
if(lastDot > 0)
{
String clientPackage = clientClass.substring(0, lastDot);
ClassLoadingMetaDataFactory cfactory = ClassLoadingMetaDataFactory.getInstance();
factory.getCapabilities().addCapability(cfactory.createModule(clientPackage));
factory.getCapabilities().addCapability(cfactory.createPackage(clientPackage));
}
//factory.setIncluded(ClassFilter.JAVA_ONLY);
ArrayList<BeanMetaDataFactory> beanFactories = new ArrayList<BeanMetaDataFactory>();
beanFactories.add(factory);
BeanMetaDataBuilder builder = BeanMetaDataBuilderFactory.createBuilder("ClientContainer", ClientContainer.class.getName());
// ClientContainer(Object metaData, String mainClass, String applicationClientName)
Object metaData = null;
builder.addConstructorParameter(Object.class.getName(), metaData);
builder.addConstructorParameter(Class.class.getName(), clientClass);
builder.addConstructorParameter(String.class.getName(), clientName);
String classLoaderName = factory.getContextName();
if(classLoaderName == null)
classLoaderName = factory.getName() + ":" + factory.getVersion();
ValueMetaData classLoader = builder.createInject(classLoaderName);
builder.setClassLoader(classLoader);
BeanMetaData clientContainerMD = builder.getBeanMetaData();
AbstractKernelDeployment deployment = new AbstractKernelDeployment();
deployment.setName(factory.getName() + ":" + factory.getVersion());
if(clientContainerMD instanceof BeanMetaDataFactory)
{
BeanMetaDataFactory bmdf = (BeanMetaDataFactory) clientContainerMD;
beanFactories.add(bmdf);
}
else
{
// Have to use the deprecated beans
ArrayList<BeanMetaData> beans = new ArrayList<BeanMetaData>();
beans.add(clientContainerMD);
deployment.setBeans(beans);
}
deployment.setBeanFactories(beanFactories);
deploy(deployment);
validate();
ClassLoader ccLoader = getBean(classLoaderName, ControllerState.INSTALLED, ClassLoader.class);
if(ccLoader == null )
throw new Exception(classLoaderName+" bean was not created");
Class<?> clientContainerClass = ccLoader.loadClass(ClientContainer.class.getName());
Object client = getBean("ClientContainer", ControllerState.INSTALLED, clientContainerClass);
if(client == null )
throw new Exception("ClientContainer bean was not created");
Method method = clientContainerClass.getMethod("getMainClass");
Class<?> mainClass = (Class) method.invoke(client);
ClassLoader mainClassLoader = mainClass.getClassLoader();
if(ccLoader != mainClassLoader)
throw new Exception(ccLoader+" != "+mainClassLoader);
// Invoke main on the underlying client main class
Class<?> parameterTypes[] = { args.getClass() };
method = clientContainerClass.getDeclaredMethod("invokeMain", parameterTypes);
method.invoke(client, (Object) args);
undeploy(deployment);
}
catch(Exception e)
{
exception = e;
}
}
}