2 Replies Latest reply on Sep 25, 2006 2:54 PM by Mauricio Lopez

    Interacting with the LoaderRepository MBean

    Mauricio Lopez Newbie

      greetings,

      i'm having a big problem with my application, because it has to be changing classes in a dynamic way, for example, the server is up and running, and someone creates a new .java archive, so, the app. has to compile it, and add it to the classpath, making it available for other module to use it. I was trying using custom class loaders, but it doesn´t work because of the problems that usually arise when using different class loaders inside the AS (ClassCastException, LinkageException, etc.).

      Seen all this problems, i saw a posible solution in the UnifiedLoaderRepository, so my application can use the Repository that the server is actually using and load the classes it needs. the question is: this kind of thing can be made?
      how do i interact with the repository from a regular class, or and ejb3 session bean packaged in a regular .jar.?

      I`m trying using something like:

      MBeanServer server = MBeanServerLocator.locate();
      try{
      ObjectName objectName = new ObjectName("org.jboss.mx.loading:name=Default");

      UnifiedLoaderRepository3MBean mbean = (UnifiedLoaderRepository3MBean)MBeanServerInvocationHandler.newProxyInstance(
      server,
      objectName, UnifiedLoaderRepository3MBean.class,
      false);
      System.out.println("obtaining class info: "+mbean.displayClassInfo("com.its.connection.ConnectionSetting"));

      }catch(Exception e){
      e.printStackTrace();

      }

      any help mould be appreciated.

        • 1. Re: Interacting with the LoaderRepository MBean
          Mauricio Lopez Newbie

          Ok, i found a work around to this problem, changing the class loader every time i need add new code to my application.

          BackGround:

          A class i the JVM is identified by de ClassLoader and the full name of the class, so everytime you make a new instance, this object is identified by the class loader that loaded the class and the class name. The problem is when you have to make several modules of your application to interact generating dynamic code on the fly.

          Whe you create a new ClassLoader object you are just pulling away the od objects and loading new ones; the old ones are going to be garvage collected and the new ones are going to be there for your use. This is not really HotSwapping at all !! because you are just creating a new loader. Any way, here is the class that do the trick inside JBOSS, using javassist and the Javassist Loader.

          Note: See the delagation of class loaders and the delegation of ClassPools.

          public class AdministradorClassPath {
          /**
          * Ruta de las librerias que deben ser cargadas dinamicamente
          */
          //TODO obtener del archivo de propiedades de configuracion de la aplicacion.
          public static final String RUTA_BODEGA = "../server/default/deploy/bodega.jar/";

          //public static final String
          protected final Log logger = LogFactory.getLog(getClass());
          //Class loader padre de la aplicacion
          private ClassPool parentClassPool;
          private static AdministradorClassPath instance;
          private ClassPool childClassPool;
          private ArrayList pathRegister;
          private LinkedHashMap<String,Class> clasesCargadas;
          private Loader loader;
          /**
          * instancia Singleton
          */
          private AdministradorClassPath(){
          setParentClassPool();
          setChildClassPool();
          //setSwapper();
          setLoader();
          pathRegister = new ArrayList();
          clasesCargadas = new LinkedHashMap<String,Class>();
          }
          /**
          * Inicializa un ClassLoader unico para la instancia del AdministradorClasspath.
          *
          */
          public void setLoader(){
          if(this.childClassPool!=null){
          loader = new Loader(Thread.currentThread().getContextClassLoader(),this.childClassPool);
          loader.delegateLoadingOf("com.its.estructuras.");
          logger.info("Cargador de creado correctamente con el pool: "+this.childClassPool);
          }
          }

          /**
          *
          * @return instace La instancia singleton de AdministradorClassPath
          */
          public static AdministradorClassPath getInstance() {
          if (instance == null) {
          instance = new AdministradorClassPath();
          }
          return instance;
          }
          /**
          * Asigna el pool de cargadores de clase de JBoss, o de cualuier contenedor
          * como el cargador padre, donde debe buscar las clases si no las encuentra en
          * uno de los cargadores hijo.
          *
          */
          private void setParentClassPool() {
          //Crea un nuevo class pool y adiciona el search path del sistema
          //Inicializa y adiciona el classpath del sistema a este pool de clases.
          this.parentClassPool = new ClassPool(true);
          }
          /**
          * Asigna un classPool hijo al ClassPool que representa el pool de clases
          * del sistema. Este pool hijo es el pricipal de la aplicacion.
          *
          */
          private void setChildClassPool() {
          childClassPool = new ClassPool(this.parentClassPool);
          childClassPool.childFirstLookup=true;

          }
          /**
          * Adiciona un nuevo Pool de clases al repositorio
          * incluyendo el pool de clases padre del ambiente
          * en el servidor de aplicaciones
          *
          */
          public void adicionarClassPath(String rutaClassPath){

          try{
          if(!pathRegister.contains(rutaClassPath)){
          childClassPool.appendClassPath(rutaClassPath);
          pathRegister.add(rutaClassPath);
          logger.debug("Se acaba de adicionar un nuevo class path al administrador: "+rutaClassPath);
          }


          }catch(NotFoundException e){
          e.printStackTrace();
          }
          }
          /**
          * Imprime los classpath que contiene el administrador de class paths.
          *
          */
          public void printClassPaths(){
          for(String path:this.pathRegister){
          logger.info(path);
          }
          }
          /**
          * Este metodo elimina el pool de clases utilizado y crea uno
          * totalmente nuevo.
          *
          */
          public void limpiarClassPoolYCrearUnoNuevo(){
          //Class Pool del sistema
          this.setParentClassPool();
          //Class Pool utilizado
          this.setChildClassPool();
          //Crear un nuevo cargador de clases
          this.setLoader();

          //Limpiar la lista de class paths almacenados
          this.pathRegister.clear();
          //Limpiar el buffer de clases que han sido cargadas utilizando el ClassPool del sistema
          this.clasesCargadas.clear();

          //Forzar recoleccion de las instancias creadas previamente
          System.gc();
          logger.info("*******************************************************************");
          logger.info("*******************************************************************");
          logger.info("********* Se ha limpiado el pool de clases por completo *********");
          logger.info("*******************************************************************");
          logger.info("*******************************************************************");
          //Adicioanar la ruta de la bodega de almacenamiento, siempre que se inicializa
          //el pool de clases.
          this.adicionarClassPath(this.RUTA_BODEGA);
          }
          /**
          * Carga la clase del PoolClases de esta clase. Para ello obtiene un CtClass que
          * luego es pasado a un objeto de tipo Class.
          *
          * @param fullClassName nombre de la clase completo
          * @return class Objeto de tipo Clase requerido
          */
          public Class cargarClase(String fullClassName){
          Class clazz=null;
          try{
          if(clasesCargadas.containsKey(fullClassName)){
          clazz = clasesCargadas.get(fullClassName);
          }else{
          clazz = loader.loadClass(fullClassName);//this.childClassPool.get(fullClassName).toClass();
          this.clasesCargadas.put(fullClassName,clazz);
          }
          }catch(ClassNotFoundException e){
          e.printStackTrace();
          }
          return clazz;
          }
          /**
          * Saca una CtClass del pool de clases.
          * @param fullClassName nombre e la clase completo.
          */
          public void sacarClaseDelPool(String fullClassName){
          try {
          CtClass cTClazz = this.childClassPool.get(fullClassName);
          cTClazz.detach();
          //ls clase se deber eliminar del buffer de clases cargadas

          } catch (NotFoundException e) {
          System.err.println("No se ha encontrado la clase solicitada.");
          e.printStackTrace();
          }
          logger.info("*********************************************************************");
          logger.info("*La clase: "+fullClassName+" ha sido sacada del pool de clases. ****");
          logger.info("*********************************************************************");
          }
          /**
          * Imprime las clases que se tienen almacenadas en el buffer de almacenamiento.
          *
          */
          public void imprimirClasesDelBuffer(){
          for(Class c:this.clasesCargadas.values()){
          logger.info(c.getName());
          }
          }

          }

          • 2. Re: Interacting with the LoaderRepository MBean
            Mauricio Lopez Newbie

            Ok, i found a work around to this problem, changing the class loader every time i need add new code to my application.

            BackGround:

            A class i the JVM is identified by de ClassLoader and the full name of the class, so everytime you make a new instance, this object is identified by the class loader that loaded the class and the class name. The problem is when you have to make several modules of your application to interact generating dynamic code on the fly.

            Whe you create a new ClassLoader object you are just pulling away the od objects and loading new ones; the old ones are going to be garvage collected and the new ones are going to be there for your use. This is not really HotSwapping at all !! because you are just creating a new loader. Any way, here is the class that do the trick inside JBOSS, using javassist and the Javassist Loader.

            Note: See the delagation of class loaders and the delegation of ClassPools.

            public class AdministradorClassPath {
            /**
            * Ruta de las librerias que deben ser cargadas dinamicamente
            */
            //TODO obtener del archivo de propiedades de configuracion de la aplicacion.
            public static final String RUTA_BODEGA = "../server/default/deploy/bodega.jar/";

            //public static final String
            protected final Log logger = LogFactory.getLog(getClass());
            //Class loader padre de la aplicacion
            private ClassPool parentClassPool;
            private static AdministradorClassPath instance;
            private ClassPool childClassPool;
            private ArrayList pathRegister;
            private LinkedHashMap<String,Class> clasesCargadas;
            private Loader loader;
            /**
            * instancia Singleton
            */
            private AdministradorClassPath(){
            setParentClassPool();
            setChildClassPool();
            //setSwapper();
            setLoader();
            pathRegister = new ArrayList();
            clasesCargadas = new LinkedHashMap<String,Class>();
            }
            /**
            * Inicializa un ClassLoader unico para la instancia del AdministradorClasspath.
            *
            */
            public void setLoader(){
            if(this.childClassPool!=null){
            loader = new Loader(Thread.currentThread().getContextClassLoader(),this.childClassPool);
            loader.delegateLoadingOf("com.its.estructuras.");
            logger.info("Cargador de creado correctamente con el pool: "+this.childClassPool);
            }
            }

            /**
            *
            * @return instace La instancia singleton de AdministradorClassPath
            */
            public static AdministradorClassPath getInstance() {
            if (instance == null) {
            instance = new AdministradorClassPath();
            }
            return instance;
            }
            /**
            * Asigna el pool de cargadores de clase de JBoss, o de cualuier contenedor
            * como el cargador padre, donde debe buscar las clases si no las encuentra en
            * uno de los cargadores hijo.
            *
            */
            private void setParentClassPool() {
            //Crea un nuevo class pool y adiciona el search path del sistema
            //Inicializa y adiciona el classpath del sistema a este pool de clases.
            this.parentClassPool = new ClassPool(true);
            }
            /**
            * Asigna un classPool hijo al ClassPool que representa el pool de clases
            * del sistema. Este pool hijo es el pricipal de la aplicacion.
            *
            */
            private void setChildClassPool() {
            childClassPool = new ClassPool(this.parentClassPool);
            childClassPool.childFirstLookup=true;

            }
            /**
            * Adiciona un nuevo Pool de clases al repositorio
            * incluyendo el pool de clases padre del ambiente
            * en el servidor de aplicaciones
            *
            */
            public void adicionarClassPath(String rutaClassPath){

            try{
            if(!pathRegister.contains(rutaClassPath)){
            childClassPool.appendClassPath(rutaClassPath);
            pathRegister.add(rutaClassPath);
            logger.debug("Se acaba de adicionar un nuevo class path al administrador: "+rutaClassPath);
            }


            }catch(NotFoundException e){
            e.printStackTrace();
            }
            }
            /**
            * Imprime los classpath que contiene el administrador de class paths.
            *
            */
            public void printClassPaths(){
            for(String path:this.pathRegister){
            logger.info(path);
            }
            }
            /**
            * Este metodo elimina el pool de clases utilizado y crea uno
            * totalmente nuevo.
            *
            */
            public void limpiarClassPoolYCrearUnoNuevo(){
            //Class Pool del sistema
            this.setParentClassPool();
            //Class Pool utilizado
            this.setChildClassPool();
            //Crear un nuevo cargador de clases
            this.setLoader();

            //Limpiar la lista de class paths almacenados
            this.pathRegister.clear();
            //Limpiar el buffer de clases que han sido cargadas utilizando el ClassPool del sistema
            this.clasesCargadas.clear();

            //Forzar recoleccion de las instancias creadas previamente
            System.gc();
            logger.info("*******************************************************************");
            logger.info("*******************************************************************");
            logger.info("********* Se ha limpiado el pool de clases por completo *********");
            logger.info("*******************************************************************");
            logger.info("*******************************************************************");
            //Adicioanar la ruta de la bodega de almacenamiento, siempre que se inicializa
            //el pool de clases.
            this.adicionarClassPath(this.RUTA_BODEGA);
            }
            /**
            * Carga la clase del PoolClases de esta clase. Para ello obtiene un CtClass que
            * luego es pasado a un objeto de tipo Class.
            *
            * @param fullClassName nombre de la clase completo
            * @return class Objeto de tipo Clase requerido
            */
            public Class cargarClase(String fullClassName){
            Class clazz=null;
            try{
            if(clasesCargadas.containsKey(fullClassName)){
            clazz = clasesCargadas.get(fullClassName);
            }else{
            clazz = loader.loadClass(fullClassName);//this.childClassPool.get(fullClassName).toClass();
            this.clasesCargadas.put(fullClassName,clazz);
            }
            }catch(ClassNotFoundException e){
            e.printStackTrace();
            }
            return clazz;
            }
            /**
            * Saca una CtClass del pool de clases.
            * @param fullClassName nombre e la clase completo.
            */
            public void sacarClaseDelPool(String fullClassName){
            try {
            CtClass cTClazz = this.childClassPool.get(fullClassName);
            cTClazz.detach();
            //ls clase se deber eliminar del buffer de clases cargadas

            } catch (NotFoundException e) {
            System.err.println("No se ha encontrado la clase solicitada.");
            e.printStackTrace();
            }
            logger.info("*********************************************************************");
            logger.info("*La clase: "+fullClassName+" ha sido sacada del pool de clases. ****");
            logger.info("*********************************************************************");
            }
            /**
            * Imprime las clases que se tienen almacenadas en el buffer de almacenamiento.
            *
            */
            public void imprimirClasesDelBuffer(){
            for(Class c:this.clasesCargadas.values()){
            logger.info(c.getName());
            }
            }

            }