1 Reply Latest reply on Nov 7, 2008 10:01 AM by deady

    Howto install custom classloader

    deady

      Hi,

      I want to write MBean deployer for dar archives, dar archive is simple jar archive with dbclasses.properties in its META-INF dir. In the properties file I'll describe db connection parameters and packages, which can be loaded from db.

      Next I want to create my classloader to fetch classes from db if they match db packages, so access to UCL for new instance of the class for the first time would call my classloader. How can I do this (I mean only classloader stuff)?

        • 1. Re: Howto install custom classloader
          deady

          For simplicity let's load class from file.

          here's a trick:

          1. create classloader

          package test.jboss;
          
          import org.jboss.logging.Logger;
          import org.jboss.mx.loading.RepositoryClassLoader;
          
          import javax.management.MalformedObjectNameException;
          import javax.management.ObjectName;
          import java.net.URL;
          import java.io.FileInputStream;
          import java.io.IOException;
          
          /**
           * User: Deady
           * Date: 06.11.2008
           * Time: 16:58:53
           */
          
          public class DBClassLoader extends RepositoryClassLoader {
          
          
           protected Logger log;
           private String name;
          
          
           /**
           * Create a new LoaderRepositoryClassLoader
           *
           * @param parent the parent classloader
           */
           public DBClassLoader(String name, URL[] url, ClassLoader parent) {
           super(url, parent);
           this.name = name;
           this.log = org.jboss.logging.Logger.getLogger(getClass().getName());
           }
          
           public ObjectName getObjectName() throws MalformedObjectNameException {
           return new ObjectName("dbclassloader");
           }
          
           public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
           log.info("loading class: "+name);
           return super.loadClass(name, resolve);
           }
          
           public URL getResourceLocally(String name) {
           log.info("getting resource locally: "+name);
           return getURL();
           }
          
           public Class loadClassLocally(String name, boolean resolve) throws ClassNotFoundException {
           log.info("loading class locally: "+name);
           String file = "/home/Deady/testbean/classes/"+name.replace(".","/")+".class";
           byte[] b = null;
           int readen = 0;
           try {
           FileInputStream in = new FileInputStream(file);
           b = new byte[in.available()];
           readen = in.read(b);
           in.close();
           } catch (IOException e) {
           e.printStackTrace();
           }
           return defineClass(name, b, 0, readen);
           }
          
           public Package[] getPackages() {
           log.info("return packages: "+getPackage(name));
           return new Package[]{getPackage(name)};
           }
          }
          



          2. create deployer MBean:


          package test.jboss;
          
          import org.jboss.deployment.DeploymentException;
          import org.jboss.deployment.DeploymentInfo;
          import org.jboss.deployment.SubDeployerSupport;
          import org.jboss.logging.Logger;
          
          import java.net.URL;
          
          /**
           * User: Deady
           * Date: 06.11.2008
           * Time: 13:42:03
           */
          public class DBDeployerMBean extends SubDeployerSupport {
          
           protected Logger log;
          
           public DBDeployerMBean() {
           this.log = org.jboss.logging.Logger.getLogger(getClass().getName());
           }
          
           public boolean accepts(DeploymentInfo di) {
           String urlStr = di.url.toString();
           String extension = getExtension();
           boolean res = urlStr.endsWith(extension);
           if (res) {
           log.info("accepted: "+di);
           }
           return res;
           }
          
          
           public void init(DeploymentInfo di) throws DeploymentException {
           log.info("initializing: "+di);
           try {
           di.createClassLoaders();
           di.ucl.getLoaderRepository().addClassLoader(new DBClassLoader("foo", new URL[]{di.localUrl}, di.ucl));
           } catch (Exception e) {
           e.printStackTrace();
           }
           super.init(di);
           }
          
           protected void deployUrl(DeploymentInfo di, URL url, String name) throws DeploymentException {
           log.info("di: "+di+", url:"+url+", name: "+name);
           super.deployUrl(di, url, name);
           }
          
           public String getExtension() {
           return ".dbr";
           }
          
          }
          


          3. create sar descriptor:

          <?xml version="1.0" encoding="UTF-8"?>
          <!--
           The JBoss service configuration file for the EJB deployer service.
          
           $Id: ejb-deployer.xml 62317 2007-04-13 10:39:22Z dimitris@jboss.org $
          -->
          <server>
          
          
           <!-- ==================================================================== -->
           <!-- DB Deployer -->
           <!-- ==================================================================== -->
          
           <!-- EJB deployer, remove to disable EJB behavior-->
           <mbean code="test.jboss.DBDeployerMBean" name="jboss.ejb:service=DBDeployer" xmbean-dd="">
          
           <!-- Inline XMBean Descriptor BEGIN -->
           <xmbean>
           <description>The DBDeployer responsible for dbr jar deployment</description>
           <class>test.jboss.DBDeployerMBean</class>
          
           <!-- ServiceMBean operations -->
           <operation>
           <description>Standard MBean lifecycle method</description>
           <name>create</name>
           </operation>
          
           <operation>
           <description>The start lifecycle operation</description>
           <name>start</name>
           </operation>
          
           <operation>
           <description>The stop lifecycle operation</description>
           <name>stop</name>
           </operation>
          
           <operation>
           <description>The destroy lifecycle operation</description>
           <name>destroy</name>
           </operation>
          
           <operation>
           <description>The detyped lifecycle operation (for internal use only)</description>
           <name>jbossInternalLifecycle</name>
           <parameter>
           <description>The lifecycle operation</description>
           <name>method</name>
           <type>java.lang.String</type>
           </parameter>
           <return-type>void</return-type>
           </operation>
          
          
           <!-- SubDeployerMBean operations -->
           <operation>
           <description>Accept a module for deployment</description>
           <name>accepts</name>
           <parameter>
           <name>info</name>
           <type>org.jboss.deployment.DeploymentInfo</type>
           </parameter>
           <return-type>boolean</return-type>
           </operation>
          
           <operation>
           <description>Initialize deployment step</description>
           <name>init</name>
           <parameter>
           <name>info</name>
           <type>org.jboss.deployment.DeploymentInfo</type>
           </parameter>
           </operation>
          
           <operation>
           <description>Create deployment step</description>
           <name>create</name>
           <parameter>
           <name>info</name>
           <type>org.jboss.deployment.DeploymentInfo</type>
           </parameter>
           </operation>
          
           <operation>
           <description>Start deployment step</description>
           <name>start</name>
           <parameter>
           <name>info</name>
           <type>org.jboss.deployment.DeploymentInfo</type>
           </parameter>
           </operation>
          
          
           <operation>
           <description>Stop deployment step</description>
           <name>stop</name>
           <parameter>
           <name>info</name>
           <type>org.jboss.deployment.DeploymentInfo</type>
           </parameter>
           </operation>
          
           <operation>
           <description>Destroy deployment step</description>
           <name>destroy</name>
           <parameter>
           <name>info</name>
           <type>org.jboss.deployment.DeploymentInfo</type>
           </parameter>
           </operation>
          
          
           </xmbean>
          
          
           </mbean>
          
          </server>
          



          4. pack sar and copy it to server:
          /jboss/server/deploy/dbdeployer.sar
          +METAINF
          ++jboss-service.xml
          +dbdeployer.jar


          5. create and deploy test.ddr - this is simple jar file:
          test.dbr
          +META-INF
          ++INDEX.LIST

          content of INDEX.LIST:
          ddd
          
          db.jar
          foo
          


          6. create test bean:
          package test;
          
          import javax.ejb.Remote;
          
          /**
           * User: Deady
           * Date: 06.11.2008
           * Time: 17:08:24
           */
          @Remote
          public interface TestClassLoader {
          
           public Object loadClass(String className);
          
          }
          


          package test;
          
          import org.jboss.annotation.ejb.RemoteBinding;
          
          import javax.ejb.Stateless;
          
          /**
           * User: Deady
           * Date: 06.11.2008
           * Time: 17:09:06
           */
          @Stateless(name="DBClassLoader")
          @RemoteBinding(jndiBinding="test/DBClassLoader/remote")
          public class TestClassLoaderImpl implements TestClassLoader{
          
           public Object loadClass(String className) {
           try {
           Class cl = Thread.currentThread().getContextClassLoader().loadClass(className);
           System.out.println("loaded = " + cl);
           Object o = cl.newInstance();
           return o;
           } catch (Exception e) {
           e.printStackTrace();
           }
           return null;
           }
          }
          
          



          7. create test class:

          package foo;
          
          import java.io.Serializable;
          
          /**
           * User: Deady
           * Date: 07.11.2008
           * Time: 17:19:00
           */
          public class Test implements Serializable {
          
           public String hello() {
           return "hello world3";
           }
          
          }
          
          


          compile it and copy to path, from which DBClassLoader will read it ("/home/Deady/testbean/classes/" in my case)


          8. test it works:

          package test;
          
          import foo.Test;
          
          import javax.naming.InitialContext;
          import javax.naming.Context;
          import java.util.Properties;
          
          /**
           * User: Deady
           * Date: 06.11.2008
           * Time: 17:16:26
           */
          public class Client {
          
           public static void main(String[] args) throws Exception {
           Properties p = new Properties();
          
          
           p.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
           p.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
           p.put(Context.PROVIDER_URL, "jnp://192.168.0.87:1099");
           InitialContext ctx = new InitialContext(p);
           TestClassLoader l = (TestClassLoader) ctx.lookup("test/DBClassLoader/remote");
           Object c = l.loadClass("foo.Test");
           if (c==null) {
           System.out.println("null");
           }
           else {
           if (c instanceof Test) {
           System.out.println(((Test)c).hello());
           }
           else {
           System.out.println(c.getClass());
           }
           }
          
          
           }
          
          }