8 Replies Latest reply on Mar 30, 2010 11:51 PM by Ryan Nideffer

    How to get EntityManager instance in a component marked @Startup

    Ryan Nideffer Newbie

      I have a component marked @Startup that reads and parses, among other things, a CSV file containing records that are inserted into my database. I am unable to retrieve an EntityManager instance to use for this though. I am still learning the nuances of Seam, but I think it is because the entityManager component is a CONVERSATION scoped component and cannot be injected into an APPLICATION scoped component, which my bean is:




      @Startup
      @Scope(ScopeType.APPLICATION)
      @Name("vsContest")
      public class VsContext {
      
          @In private EntityManager entityManager;
      
          @PostConstruct
          public void initialize() {
              // entityManager is null!!
          }
      }




      The one place in my application so far that I am able to successfully inject an instance of entityManager is in my authenticator component, so I attempted to repeat the pattern for this. I created a new SLSB and attempted to inject THAT:


      @Local
      public interface CredentialsProcessor {
          public void process();
      }



      and


      @Name("csvCredentialsProcessor")
      @Stateless
      public class CSVCredentialsProcessor implements CredentialsProcessor {
          private static final Log log = LogFactory.getLog(CSVCredentialsProcessor.class);
          @In
          private EntityManager entityManager;
          private InputStream csvCreds;
          
          public CSVCredentialsProcessor() {}
          
          public CSVCredentialsProcessor(InputStream csvCreds) {
              log.debug("constructor - entityManager.toString(): " + entityManager.toString());
          }
      
          public void process() {
              log.debug("process - entityManager.toString(): " + entityManager.toString());
          }
      }



      then added this to VsContext:


      @In private CredentialsProcessor csvCredentialsProcessor;
      



      And still no go.


      What is the proper way to go about this?


      Thanks,
      Ryan

        • 1. Re: How to get EntityManager instance in a component marked @Startup
          Ryan Nideffer Newbie

          I found that I can explicitly retrieve an entityManager instance by doing:


          EntityManager em = (EntityManager)Component.getInstance("entityManager");



          Then, just to test this out, I am doing this:


          User u = new User();
          u.setFirstName("joe");
          u.setMiddleName("");
          u.setLastName("blow");
          u.setUsername("jblow");
          u.setPin("1234");
                      
          em.persist(u);



          However, the new User is never persisted. My User entity is properly written, because I am able to login against the rows stored in the database, with the same User object.


          I attempted to add em.flush() to my code, but then a javax.persistence.TransactionRequiredException is thrown, with the message no transaction in progress.


          I tried to change the FlushModeType to no avail, then read that for a given persistence unit, any pending commits will be written to the database prior to a query executing. So I attempted to login to my application (which performs a query against an injected EntityManager instance) for my newly created user, and sure enough, login fails, no user found.


          Any ideas?


          Thanks,
          Ryan

          • 2. Re: How to get EntityManager instance in a component marked @Startup
            Nikolay Elenkov Master

            Instead of using PostConstruct, write an observer for org.jboss.seam.postInitialization and do your processing there. To make things easier use an EJB and @PersitenceContext, that would handle transactions properly. There is no point in using the SMPC for your use case.


            HTH

            • 3. Re: How to get EntityManager instance in a component marked @Startup
              Stuart Douglas Master

              Try this:



              @Startup
              @Scope(ScopeType.APPLICATION)
              @Name("vsContest")
              public class VsContext {
              
                  @In private EntityManager entityManager;
              
                  @Create
                  @Transactional
                  public void initialize() {
                      User u = new User();
                      u.setFirstName("joe");
                      u.setMiddleName("");
                      u.setLastName("blow");
                      u.setUsername("jblow");
                      u.setPin("1234");
                          
                      entityManager.persist(u);
                      entityManager.flush();
                  }
              }
              
              

              • 4. Re: How to get EntityManager instance in a component marked @Startup
                Ryan Nideffer Newbie

                Nikolay/Stuart-


                Thank you very much for your help, I believe this will work however I have not tried it yet because I changed the structure of my application since the original post. I still have the vsContext component annotated with @Startup, and my initialization method is changed to be an @Observer for postInitialization, but now what I am doing is using the Factory pattern to retrieve a user credentials processor object. It is in this object now that I need to utilize the entity manager:


                public interface CredentialsProcessor {
                    
                    public void process(InputStream in);
                    
                }



                and for a CSV file:


                @Scope(APPLICATION)
                @Name("csvCredentialsProcessor")
                public class CSVCredentialsProcessor implements CredentialsProcessor {
                
                    @In private EntityManager entityManager;
                
                    public void process(InputStream in) {
                    ...
                    }
                }



                My factory class is a standard impl that keeps a single instance of CredentialsProcessor for each type of credentials file that we accept.


                But I think I am going against the Seam component model by managing my own instances with the factory pattern. What is the proper way to do this with seam? Because I need the flow of control to proceed from my component annotated @Startup, to retrieving the proper CredentialsProcessor impl, and having access to an EntityManager.


                Thanks!

                • 5. Re: How to get EntityManager instance in a component marked @Startup
                  Ryan Nideffer Newbie

                  I've attempted to use Nikolay's suggestion of EJB w/PersistenceContext, and I feel like I'm back to square one: no EntityManager is injected.


                  Here is my factory:


                  public class CredentialsProcessorFactory {
                      
                      private static Map<CredentialsType, CredentialsProcessor> PROCESSORS = 
                          new HashMap<CredentialsType, CredentialsProcessor>();
                      
                      static {
                          PROCESSORS.put(CredentialsType.CSV, new CSVCredentialsProcessor());
                      }
                  
                      private CredentialsProcessorFactory() {}
                      
                      public static CredentialsProcessor getProcessor(CredentialsType type) {
                          CredentialsProcessor p = PROCESSORS.get(type);
                          if(p == null)
                              throw new IllegalArgumentException("No CredentialsProcessor registered for type " + type.toString());   
                          return p;
                      }  
                  }



                  There is only a single CredentialsProcessor in the factory store as of now, a CSVCredentialsProcessor. I've made this an EJB:


                  @Local
                  public interface CredentialsProcessor {   
                      public void process(InputStream in);
                  }



                  @Stateless
                  public class CSVCredentialsProcessor implements CredentialsProcessor {
                      @PersistenceContext private EntityManager entityManager;
                  
                      public void process(InputStream creds) { ... }
                  }



                  After my instance is returned via CredentialsProcessorFactory.getProcessor(CredentialsType.CSV), the entityManager instance is still null.


                  I really feel like I'm fighting Seam here, way more than I should be. Do I need to give up, and make every single object a component?

                  • 6. Re: How to get EntityManager instance in a component marked @Startup
                    Ryan Nideffer Newbie

                    I should have waited before posting. So obviously the instance I am instantiating and storing in the static Map is not managed by JBoss and will not have any interception or injection performed on it.


                    Explicit lookup of the SLSB from JNDI returns an instance that has the EntityManager injected properly.


                    My question now becomes: with EJB3 and JBoss what is corresponding Factory pattern? I'd like to continue to be able to use the CredentialsType enum to lookup the proper implementation. Is an explicit JNDI lookup the proper way to do this?


                    Thanks,
                    Ryan

                    • 7. Re: How to get EntityManager instance in a component marked @Startup
                      Nikolay Elenkov Master

                      Ryan N wrote on Mar 30, 2010 01:22:


                      I should have waited before posting. So obviously the instance I am instantiating and storing in the static Map is not managed by JBoss and will not have any interception or injection performed on it.


                      Right, for injection to work you need to get SBs with @EJB or lookup from JNDI. For Seam components, you need to create them with @In(create=true) or use Component.getInstance().



                      My question now becomes: with EJB3 and JBoss what is corresponding Factory pattern? I'd like to continue to be able to use the CredentialsType enum to lookup the proper implementation. Is an explicit JNDI lookup the proper way to do this?



                      Since your component is an SLSB it is already pooled by the container. So instead of caching those in a map, just return a fresh instance. If you decide to make it a Seam component, you can look it up by name. Something like:


                      public static CredentialsProcessor getProcessor(CredentialsType type) {
                        swtich(type) {
                          CSV:
                             return Component.getInstance("csvProcessor", true);
                             // or class: Component.getInstance(CSVCredentialsProcessor.class, true);
                          XML:
                             return Component.getInstance("xmlProcessor", true);
                          default:
                           // throw?
                        }



                      If you stick with 'plain' EJB, use JNDI or inject all the processors you need if there is only a few.


                      Generally, you can safely make all your SBs Seam components (add @Name). That way you get the best of both worlds.


                      HTH