4 Replies Latest reply on Oct 8, 2008 11:44 AM by matt10

    Accessing EntityManagerFactory/SessionFactory via JNDI serve

    matt10

      Hi,

      I have an application framework which scans the EAR it is contained in for EJB3 entity bean classes and persistence units, to auto-detect them, in order to provide access to data through a facade SLSB.

      I need to obtain the following programmatically:
      1. A list of all Persistence Units
      2. A list of all Entity Bean classes
      3. The container-managed EntityManager for each Persistence Unit

      Injecting the EntityManagers using annotations on beans is no good here because the PUs and Entity Bean classes will not be known by the application framework I am writing.
      I want the facade to access data given only the simple class name of the entity bean.

      I have a working theory of how to get the above but I've hit a blocking problem I can't get over during implementation.

      In theory:
      - I can get a list of all PUs from JNDI, as well as references to the SessionFactory or EntityManagerFactory objects.
      - I can get a list of all entity classes in each PU by calling SessionFactory.getAllClassMetadata() on the SessionFactory for each persistence unit.
      - I can get a SessionFactory from an EntityManagerFactory if I need to.
      - I can get an EntityManagerFactory from an EntityManager if I need to.

      I have tried to get a list of Persistence Unit names by scanning JNDI; either looking for the standard signature:

      persistence.units:ear=TestEAR.ear,jar=TestApp.jar,unitName=default


      .. or by explicitly registering all PUs in a JNDI folder e.g.
      persistence-units/PU1
      persistence-units/PU2



      Both JNDI entries should provide access to SessionFactory or EntityManagerFactory objects as per specs and documentation I've read, but the bound object is always an instance of javax.naming.Reference instead of the className I expect: org.jboss.ejb3.entity.InjectedEntityManagerFactory, org.hibernate.impl.SessionFactoryImpl, or the interfaces; javax.persistence.EntityManager, EntityManagerFactory or SessionFactory. Any of these objects from JNDI would do, I've tried looking up them all.

      I've looked all over the place for a solution to this javax.naming.Reference problem. I figured it might be due to the class loader isolation I have configured with this snippet in jboss-app.xml:
      <loader-repository>org.baselib:loader=Test.jar<loader-repository-config>
       java2ParentDelegation=false
       </loader-repository-config>
       </loader-repository>
      


      I need this isolation because I use a different version of BeanShell in my EAR than the one that ships with Jboss (using Jboss AS 4.2.0 at present; I have issues with most other versions except for 4.2.1). I have tried removing it, and verifying that isolation is not the default in ear-deployer.xml, but I still get javax.naming.Reference.

      Other posts I've found refer to classpath problems with the client jar. However this code is running on the server so (if I understand how things work correctly) access to all the jboss classes is already available.

      I'm currently working with a simple test case: TestBean.java that tries to look up EntityManagerFactories in JNDI:

      @Service
      @Management(IServiceManagement.class)
      public class TestBean implements IServiceManagement {
      
       public void create() throws Exception {}
      
       public void destroy() {}
      
       public void start() throws Exception {
       logger.info("Starting TestBean service");
       listAllPersistenceUnits();
       }
      
       public static void listAllPersistenceUnits() throws Exception {
       InitialContext ctx = new InitialContext();
       NamingEnumeration<Binding> list = ctx.listBindings("persistence-units");
       while (list.hasMore()) {
       Binding nc = (Binding)list.next();
       logger.info("Persistence Unit EMF: "+nc.getName()+" [class: "+nc.getClassName()+"]");
       Object o = nc.getObject();
       if(!(o instanceof EntityManagerFactory))
       logger.warn("No good; o instance of "+o.getClass().getName()); // javax.naming.Reference
       }
       }
      
       public void stop() {}
      
       protected static final Logger logger = Logger.getLogger(TestBean.class.getName());
      
      }
      


      persistence.xml for the test looks like this:
       <persistence-unit name="default" transaction-type="JTA">
       <jta-data-source>java:/ApplicationDS</jta-data-source>
       <properties>
       <property name="hibernate.hbm2ddl.auto" value="none"/>
       <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
       <property name="jboss.entity.manager.factory.jndi.name" value="persistence-units/default"/>
       <!-- testing:
       <property name="jboss.entity.manager.jndi.name" value="persistence-units/default"/>
       <property name="hibernate.session_factory_name" value="java:/session-factories/" /> -->
       </properties>
       </persistence-unit>
      


      The output looks like this:
      11:44:38,846 INFO [EJBContainer] STARTED EJB: org.foo.test.TestBean ejbName: TestBean
      11:44:38,886 INFO [TestBean] Starting TestBean service
      11:44:38,900 INFO [TestBean] Persistence Unit EMF: default [class: org.jboss.ejb3.entity.InjectedEntityManagerFactory]
      11:44:38,901 WARN [TestBean] No good; o instance of javax.naming.Reference
      


      I've been struggling with this problem for weeks.

      Can anybody help me fix JNDI access, or indicate another solution for getting the container-managed EntityManager for a persistence unit programmatically?

      Regards,
      Matt

        • 1. Re: Accessing EntityManagerFactory/SessionFactory via JNDI s
          jaikiran

          How about changing your code to:

          InitialContext ctx = new InitialContext();
          NamingEnumeration<Binding> list = ctx.listBindings("persistence-units");
          while (list.hasMore()) {
           Binding nc = (Binding)list.next();
           System.out.println("Persistence Unit EMF is relative : " + nc.isRelative() + " name: " + nc.getName()+" [class: "+nc.getClassName()+"]");
          
           Object o = ctx.lookup("persistence-units/" + nc.getName());
          
           if(!(o instanceof EntityManagerFactory)) {
           System.out.println("No good; o instance of "+o.getClass().getName());
           }
           else {
           System.out.println("Got it again!");
           }
          }


          • 2. Re: Accessing EntityManagerFactory/SessionFactory via JNDI s
            matt10

            That works.

            Thanks so much, jaikiran! :)

            So the problem is that:..

            NamingEnumeration<Binding> list = ctx.listBindings("persistence-units");
            while (list.hasMore()) {
             Binding nc = (Binding)list.next();
             Object o = nc.getObject();
             ...
            


            ... does not return the same object as:

            NamingEnumeration<Binding> list = ctx.listBindings("persistence-units");
            while (list.hasMore()) {
             Binding nc = (Binding)list.next();
             Object o = ctx.lookup("persistence-units/" + nc.getName());
             ...
            


            I didn't know this at all.

            I was coding referring to Javadocs and some web examples only.. I usually only do a simple lookup in JNDI not scan it. I even read the JNI lookup source code in JBoss to see when and how javax.naming.Reference instances are resolved.

            Another one to put down to experience.

            Thanks.

            Matt

            • 3. Re: Accessing EntityManagerFactory/SessionFactory via JNDI s
              fiorenzino

              Hi Matt,
              i would read all named Queries presents in my SessionFactory.
              I do not know how to retrieve this information using a entity manager.
              (....Someone know a trick for this??...)

              When I read your message, I thought you can use your solution. Read in the ClassMetadata, the names of persistent classes, to search for entries named Queries

              You wrote:
              - I can get a list of all PUs from JNDI, as well as references to the SessionFactory or EntityManagerFactory objects.
              - I can get a list of all entity classes in each PU by calling SessionFactory.getAllClassMetadata() on the SessionFactory for each persistence unit.

              How?

              Thanks in advance

              Fiorenzo

              • 4. Re: Accessing EntityManagerFactory/SessionFactory via JNDI s
                matt10

                Hi Fiorenzo,

                You can get a list of entity classes from an EntityManager. Then add code to scan the @NamedQuery and @NamedQueries({ ... }) annotations on those classes and make a list of NamedQuery names.

                Like this:

                // Get all entity classes
                org.hibernate.SessionFactory sf = ((org.jboss.ejb3.entity.HibernateSession) em).getHibernateSession().getSessionFactory();
                Map<?,?> allClassMetadata = sf.getAllClassMetadata();
                List<String> queries = new ArrayList<String>();
                for (Object value: allClassMetadata.values()) {
                 EntityPersister ep = (EntityPersister) value;
                 Class<?> entityClass = ep.getClassMetadata().getMappedClass(EntityMode.POJO);
                 NamedQuery nqAnno = entityClass.getAnnotation(NamedQuery.class);
                 if(nqAnno != null)
                 queries.add(nqAnno.name());
                 NamedQueries nqsAnno = entityClass.getAnnotation(NamedQueries.class);
                 if(nqsAnno != null) {
                 for(NamedQuery q: nqsAnno.value())
                 queries.add(q.name());
                 }
                }
                


                I haven't tested this code.

                Good luck.

                Regards,
                Matt