Index: /Users/svn/JBossHead/jboss-head/ejb3/build.xml =================================================================== --- /Users/svn/JBossHead/jboss-head/ejb3/build.xml (revision 76821) +++ /Users/svn/JBossHead/jboss-head/ejb3/build.xml (working copy) @@ -94,6 +94,7 @@ + Index: /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/client/ClientContainer.java =================================================================== --- /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/client/ClientContainer.java (revision 76821) +++ /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/client/ClientContainer.java (working copy) @@ -30,6 +30,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -53,7 +54,6 @@ import org.jboss.injection.InjectionContainer; import org.jboss.injection.InjectionHandler; import org.jboss.injection.Injector; -import org.jboss.injection.JndiInjectHandler; import org.jboss.injection.PersistenceUnitHandler; import org.jboss.injection.WebServiceRefHandler; import org.jboss.logging.Logger; @@ -103,6 +103,9 @@ { log.info("ClientContainer(version="+VERSION+")"); log.info("DependencyPolicy.CS: "+DependencyPolicy.class.getProtectionDomain().getCodeSource()); + log.info("ClientContainer.CS: "+getClass().getProtectionDomain().getCodeSource()); + ClassLoader mainClassLoader = mainClass.getClassLoader(); + log.info("mainClass.ClassLoader: "+mainClassLoader); clientJndiEnv.set(jndiEnv); this.xml = xml; this.mainClass = mainClass; @@ -110,6 +113,8 @@ ClientJavaEEComponent client = new ClientJavaEEComponent(applicationClientName); this.dependsPolicy = new NoopDependencyPolicy(client); + URL jndiPropertiesURL = mainClassLoader.getResource("jndi.properties"); + log.info("mainClassLoader jndi.properties: "+jndiPropertiesURL); Context ctx = InitialContextFactory.getInitialContext(jndiEnv); enc = (Context) ctx.lookup(applicationClientName); StringBuffer encInfo = new StringBuffer("Client ENC("+applicationClientName+"):\n"); @@ -289,9 +294,19 @@ { Class parameterTypes[] = { args.getClass() }; Method method = mainClass.getDeclaredMethod("main", parameterTypes); - method.invoke(null, (Object) args); + try + { + log.info("Invoking main: "+method); + method.invoke(null, (Object) args); + log.info("Successfully invoked main"); + } + catch(Throwable e) + { + e.printStackTrace(); + log.error("Invocation of client main failed", e); + } } - + /** * Call post construct methods. * @throws IllegalAccessException Index: /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/client/ClientLauncher.java =================================================================== --- /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/client/ClientLauncher.java (revision 76821) +++ /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/client/ClientLauncher.java (working copy) @@ -22,8 +22,13 @@ package org.jboss.ejb3.client; import java.io.IOException; -import java.net.URL; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.rmi.server.RMIClassLoader; +import java.rmi.server.RMIClassLoaderSpi; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Properties; @@ -30,10 +35,32 @@ import javax.naming.InitialContext; import javax.naming.NamingException; +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.system.DefaultClassLoaderSystem; +import org.jboss.classloader.spi.ClassLoaderDomain; +import org.jboss.classloader.spi.ClassLoaderSystem; +import org.jboss.classloader.spi.ParentPolicy; +import org.jboss.classloading.spi.dependency.ClassLoading; +import org.jboss.classloading.spi.vfs.dependency.VFSClassLoaderPolicyModule; +import org.jboss.classloading.spi.vfs.metadata.VFSClassLoaderFactory; +import org.jboss.classloading.spi.vfs.metadata.VFSClassLoaderFactory10; import org.jboss.client.AppClientLauncher; +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; import org.jboss.metadata.client.jboss.JBossClientMetaData; -import org.jboss.util.NotImplementedException; import org.jboss.xb.binding.JBossXBException; /** @@ -51,7 +78,13 @@ implements AppClientLauncher { private static final Logger log = Logger.getLogger(ClientLauncher.class); - + private static Throwable exception; + /** The kernel */ + private static Kernel kernel; + + /** The deployer */ + private static BasicXMLDeployer deployer; + /** * Convenience method for launching a client container. * @@ -62,28 +95,12 @@ * @throws Exception */ public static void launch(JBossClientMetaData xml, String mainClassName, String applicationClientName, String args[]) - throws Exception + throws Throwable { - launch(xml, mainClassName, applicationClientName, args, null); + List cp = Collections.emptyList(); + launch(xml, cp, mainClassName, applicationClientName, args, null); } - public static void launch(JBossClientMetaData xml, String mainClassName, - String applicationClientName, String args[], Properties jndiEnv) - throws Exception - { - Class mainClass = Class.forName(mainClassName); - // Pass in the jndi env properties so InitialContext() works - if(jndiEnv != null) - { - for(Object key : jndiEnv.keySet()) - { - String name = (String) key; - System.setProperty(name, jndiEnv.getProperty(name)); - } - } - ClientContainer container = new ClientContainer(xml, mainClass, applicationClientName, jndiEnv); - - container.invokeMain(args); - } + /** * Convenience method to load the XML descriptor. @@ -94,13 +111,6 @@ */ public static JBossClientMetaData loadXML(String applicationClientName) throws NamingException { - /* - URL url = findResource("META-INF/application-client.xml"); - log.trace("application-client.xml found at " + url); - URL jbossClientURL = findResource("META-INF/jboss-client.xml"); - log.trace("jboss-client.xml found at " + jbossClientURL); - return loadXML(url, jbossClientURL); - */ log.warn("FIXME: using an unsupported hack to get metadata"); InitialContext ctx = new InitialContext(); JBossClientMetaData metaData = (JBossClientMetaData) ctx.lookup(applicationClientName + "/metaData"); @@ -106,60 +116,251 @@ JBossClientMetaData metaData = (JBossClientMetaData) ctx.lookup(applicationClientName + "/metaData"); return metaData; } - + public static List loadClassPath(String applicationClientName) + throws NamingException + { + InitialContext ctx = new InitialContext(); + List cp = (List) ctx.lookup(applicationClientName + "/classPathEntries"); + return cp; + } + /** - * Work in progress. + * The AppClientLauncher method for launching a client container. * - * @param args the arguments for the launcher + * @param mainClassName - the class whose main(String[]) will be invoked + * @param clientName - the client name that maps to the server side JNDI ENC + * @param args - the args to pass to main method + * @throws Throwable */ - public static void main(String[] args) + public void launch(String mainClassName, String clientName, String args[]) + throws Throwable + { + launch(mainClassName, clientName, args, null); + } + public void launch(String mainClassName, String clientName, String args[], + Properties jndiEnv) + throws Throwable + { + // Set the RMIClassLoaderSpi implementation to JBossRMIClassLoader + System.setProperty("java.rmi.server.RMIClassLoaderSpi", JBossRMIClassLoader.class.getName()); + + JBossClientMetaData xml = loadXML(clientName); + List cp = loadClassPath(clientName); + launch(xml, cp, mainClassName, clientName, args, jndiEnv); + } + + /** + * The client launcher entry point that create an mc to launch the client container. + * @param clientClass + * @param clientName + * @param cp + * @param args + * @throws Throwable + */ + public static void launch(JBossClientMetaData xml, List classPath, + String mainClassName, String applicationClientName, String args[], + Properties jndiEnv) + throws Throwable { + // Init the kernel and deployers + init(); + + // Pass in the jndi env properties so InitialContext() works + if(jndiEnv != null) + { + for(Object key : jndiEnv.keySet()) + { + String name = (String) key; + System.setProperty(name, jndiEnv.getProperty(name)); + } + } + + ArrayList beanFactories = new ArrayList(); + ArrayList beans = new ArrayList(); + + // Add the common launcher beans, ClassLoaderSystem + BeanMetaDataBuilder builder = BeanMetaDataBuilderFactory.createBuilder("ClassLoaderSystem", ClassLoaderSystem.class.getName()); + builder.setFactoryClass(ClientLauncher.class.getName()); + builder.setFactoryMethod("getClassLoaderSystem"); + BeanMetaData classLoaderSystemBMD = builder.getBeanMetaData(); + addBeanMetaData(beanFactories, beans, classLoaderSystemBMD); + + // ClassLoading + builder = BeanMetaDataBuilderFactory.createBuilder("ClassLoading", ClassLoading.class.getName()); + builder.addMethodInstallCallback("addModule", ControllerState.CONFIGURED); + builder.addMethodUninstallCallback("removeModule", ControllerState.CONFIGURED); + BeanMetaData classLoadingBMD = builder.getBeanMetaData(); + addBeanMetaData(beanFactories, beans, classLoadingBMD); + try { - if(args.length < 1) - throw new IllegalArgumentException("expected a jar filename as argument"); - - Class mainClass; - - String name = args[0]; - if(name.endsWith(".jar")) + builder = BeanMetaDataBuilderFactory.createBuilder("ClientContainer", + "org.jboss.ejb3.client.ClientContainer"); + VFSClassLoaderFactory factory = new VFSClassLoaderFactory("ClientLauncherClassPath"); + ArrayList roots = new ArrayList(); + // Create the classpath + log.debug("Setting up classpath from: "); + for(String path : classPath) { - throw new NotImplementedException(); -// JarFile jarFile = new JarFile(jarName); + log.debug(path); + roots.add(path); } - else + factory.setRoots(roots); + beanFactories.add(factory); + // ClientContainer(xml, mainClass, applicationClientName, jndiEnv); + builder.addConstructorParameter(JBossClientMetaData.class.getName(), xml); + builder.addConstructorParameter(Class.class.getName(), mainClassName); + builder.addConstructorParameter(String.class.getName(), applicationClientName); + builder.addConstructorParameter(Properties.class.getName(), jndiEnv); + // Use vfs class loader as the ClientContainer class loader + 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()); + addBeanMetaData(beanFactories, beans, clientContainerMD); + deployment.setBeanFactories(beanFactories); + if(beans.size() > 0) + deployment.setBeans(beans); + deploy(deployment); + + KernelController controller = kernel.getController(); + // ClientContainer + KernelControllerContext context = (KernelControllerContext) controller.getContext("ClientContainer", ControllerState.INSTALLED); + if (context == null) + throw new Exception("ClientContainer bean was not created"); + Object client = context.getTarget(); + KernelControllerContext cclContext = (KernelControllerContext) controller.getContext(classLoaderName, ControllerState.INSTALLED); + if (cclContext == null) + throw new Exception(classLoaderName+" bean was not created"); + ClassLoader ccLoader = (ClassLoader) cclContext.getTarget(); + if (ccLoader == null ) + throw new Exception(classLoaderName+" bean was not created"); + if (client.getClass().getClassLoader() != ccLoader) + log.warn(client.getClass().getClassLoader()+" != "+ccLoader); + Class clientContainerClass = ccLoader.loadClass("org.jboss.ejb3.client.ClientContainer"); + if (clientContainerClass.getClassLoader() != ccLoader) + log.warn(clientContainerClass.getClassLoader()+" != "+ccLoader); + + // Invoke main on the underlying client main class through the ClientContain + ClassLoader prevLoader = Thread.currentThread().getContextClassLoader(); + try { - String mainClassName = name; - mainClass = Class.forName(mainClassName); + Thread.currentThread().setContextClassLoader(ccLoader); + Class parameterTypes[] = { args.getClass() }; + Method invokeMain = clientContainerClass.getDeclaredMethod("invokeMain", parameterTypes); + invokeMain.invoke(client, (Object) args); } - - URL appXmlURL = mainClass.getClassLoader().getResource("META-INF/application-client.xml"); - if(appXmlURL == null) - throw new RuntimeException("Can't find META-INF/application-client.xml"); - - // FIXME: client metadata - JBossClientMetaData xml = null; - //JBossClientMetaData xml = ApplicationClientDDObjectFactory.parse(appXmlURL); - if(true) - throw new RuntimeException("NYI"); - - // FIXME: j2ee.clientName - - List newArgs = new ArrayList(); - for(int i = 1; i < args.length; i++) + finally { - newArgs.add(args[i]); + Thread.currentThread().setContextClassLoader(prevLoader); } - args = newArgs.toArray(args); - - // FIXME: when jar gets implemented this won't work anymore - String mainClassName = name; - launch(xml, mainClassName, "FIXME", args); + + // + undeploy(deployment); + } + catch(Throwable e) + { + exception = e; + throw e; + } + } + + /** + * Create a ClassLoaderSystem with the default ClassLoaderDomain set to use + * a AFTER ParentPolicy. + * + * @return ClassLoaderSystem instance + */ + public static ClassLoaderSystem getClassLoaderSystem() + { + DefaultClassLoaderSystem system = new DefaultClassLoaderSystem(); + ClassLoaderDomain defaultDomain = system.getDefaultDomain(); + defaultDomain.setParentPolicy(ParentPolicy.AFTER); + return system; + } + + private static void addBeanMetaData( + ArrayList beanFactories, + ArrayList beans, BeanMetaData bmd) + { + // TODO Auto-generated method stub + if(bmd instanceof BeanMetaDataFactory) + { + BeanMetaDataFactory bmdf = (BeanMetaDataFactory) bmd; + beanFactories.add(bmdf); + } + else + { + // Have to use the deprecated beans + beans.add(bmd); + } + } + + private static void init() throws Throwable + { + // Bootstrap the kernel + AbstractBootstrap bootstrap = new BasicBootstrap(); + bootstrap.run(); + kernel = bootstrap.getKernel(); + + // Create the deployer + deployer = createDeployer(); + + } + + private static BasicXMLDeployer createDeployer() + { + return new BasicXMLDeployer(kernel, ControllerMode.AUTOMATIC); + } + + /** + * 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) { - e.printStackTrace(); - System.exit(1); + 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); } } @@ -164,24 +365,122 @@ } /** - * The AppClientLauncher method for launching a client container. + * Validate * - * @param mainClassName - the class whose main(String[]) will be invoked - * @param clientName - the client name that maps to the server side JNDI ENC - * @param args - the args to pass to main method - * @throws Throwable + * @throws Exception for any error */ - public void launch(String mainClassName, String clientName, String args[]) - throws Throwable + private static void validate() throws Exception { - launch(mainClassName, clientName, args, null); + 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); + } } - public void launch(String mainClassName, String clientName, String args[], - Properties jndiEnv) - throws Throwable + + public static void main(String[] args) + { + String[] roots = { + "vfszip:/home/svn/JBossHead/jboss-head/build/output/jboss-5.0.0.CR2/server/cts/tmp/jsr88/assembly_classpath_appclient.ear/assembly_classpath_appclient_client.jar", + "vfszip:/Users/svn/JBossHead/jboss-head/build/output/jboss-5.0.0.CR2/server/cts/tmp/jsr88/assembly_classpath_appclient.ear/libs/direct_classpath_util.jar", + "vfszip:/Users/svn/JBossHead/jboss-head/build/output/jboss-5.0.0.CR2/server/cts/tmp/jsr88/assembly_classpath_appclient.ear/libs/indirect_classpath_util.jar" + }; + VFSClassLoaderFactory10 factory = new VFSClassLoaderFactory10(); + factory.setRoots(Arrays.asList(roots)); + VFSClassLoaderPolicyModule module = new VFSClassLoaderPolicyModule(factory, "AppClientLoaderModule"); + + } + + /** + * RMIClassLoaderSpi that uses the thread context class loader + * + * @author Adrian Brock + * @author Scott.Stark@jboss.org + * @version $Revision:$ + */ + public static class JBossRMIClassLoader + extends RMIClassLoaderSpi { - JBossClientMetaData xml = loadXML(clientName); - launch(xml, mainClassName, clientName, args, jndiEnv); + // Attributes ---------------------------------------------------- + + /** + * The JVM implementation (we delegate most work to it) + */ + RMIClassLoaderSpi delegate = RMIClassLoader.getDefaultProviderInstance(); + + // Constructors -------------------------------------------------- + + /** + * Required constructor + */ + public JBossRMIClassLoader() + { + } + + // RMIClassLoaderSpi Implementation ------------------------------ + + /* + * Ignore the JVM, use the thread context classloader for proxy caching + */ + public Class loadProxyClass(String codebase, String[] interfaces, ClassLoader ignored) + throws MalformedURLException, ClassNotFoundException + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + return delegate.loadProxyClass(codebase, interfaces, loader); + } + + /* + * Just delegate + */ + public Class loadClass(String codebase, String name, ClassLoader ignored) + throws MalformedURLException, ClassNotFoundException + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + return delegate.loadClass(codebase, name, loader); + } + + /* + * Just delegate + */ + public ClassLoader getClassLoader(String codebase) + throws MalformedURLException + { + return delegate.getClassLoader(codebase); + } + + /* + * Try to delegate an default to the java.rmi.server.codebase on any + * failure. + */ + public String getClassAnnotation(Class cl) + { + String annotation = null; + try + { + annotation = delegate.getClassAnnotation(cl); + } + catch(Throwable t) + { + // Try the java.rmi.server.codebase property + annotation = System.getProperty("java.rmi.server.codebase"); + } + return annotation; + } } } Index: /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/deployers/Ejb3ClientDeployer.java =================================================================== --- /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/deployers/Ejb3ClientDeployer.java (revision 76821) +++ /Users/svn/JBossHead/jboss-head/ejb3/src/main/org/jboss/ejb3/deployers/Ejb3ClientDeployer.java (working copy) @@ -21,6 +21,7 @@ */ package org.jboss.ejb3.deployers; +import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -107,7 +108,14 @@ } // Notify the client launcher of extra class path entries in an EAR (See EE 8.2) - encCtx.bind("classPathEntries", getClassPathEntries(unit)); + List classPath = unit.getClassPath(); + ArrayList cpURIs = new ArrayList(); + for(VirtualFile vf : classPath) + { + String uri = vf.toURI().toString(); + cpURIs.add(uri); + } + encCtx.bind("classPathEntries", cpURIs); // java:comp/UserTransaction -> UserTransaction Util.createLinkRef(encCtx, "UserTransaction", "UserTransaction");