8 Replies Latest reply on Apr 5, 2009 4:02 AM by Arbi Sookazian

    EJB stateless bean injection

    Wolfgang Bergbauer Newbie

      Hi all,


      I want to inject/utilize a stateless session bean in another stateful session bean.
      Use Case: Small wizard that triggers a asynchronous process. The wizard takes some user input, submits a record to a db, and gets the result via a JMS message.


      1. start conversation that is handled by SFSB (seam component meAccountWizardBean)
      2. at a certain point, use a SLSB (seam component tibcoJMSTopicSubscriber) to subscribe to a JMS queue and wait for 30 seconds to get the result. The SLSB is only needed for this one method call, so I guess the ideal scope would be the EVENT scope.


      Now, I tried a lot of solution, of which some are working. When I make the SLSB a pojo and inject it into the SFSB it works. But why not using the container to manage the queue receiver bean and just inject it. The only way I got this working is by using the @EJB annotation in the SFSB. Whenever I use @In, it does not work.
      Now even @In if it would work, I think is not ideal. I only need this bean during one method call. Using @In to inject a Event scoped bean into a conversation scope bean would be a lot of overhead. During each step of the wizard, the EVENT scoped bean would be injected over and over again.
      So my plan is to lookup the bean via the API.


      Now here is the solution is would like to get working.


      SFSB


      @Stateless
      @Name("tibcoJMSTopicSubscriber")
      public class TibcoJMSTopicSubscriber implements TibcoJMSTopicSubscriberIf{
      
          String      serverUrl       = "tcp://aixbomsdev:7222";
          String      userName        = null;
          String      password        = null;
          String      queueName       = "queue.sample2";
          String           messageText;
          
          
          public javax.jms.Message subscribe() {
          // this method subscribes to a queue and waits for 30 seconds to receive a message
      
      





      SFSB
      
      @Stateful 
      @Name("meAccountWizardBean")
      public class MeAccountWizardBean implements MeAccountWizard {
      
           @Logger
           private Log log;
           
           @PersistenceContext
           EntityManager em;
           
              // below code works with a POJO seam component, not with SLSB
           //@In(create=true)
           //TibcoJMSTopicSubscriberIf tibcoJMSTopicSubscriber;
           
           
           public String subscribe()
           {     
                // retreive the SLSB via the Stateless context.
                TibcoJMSTopicSubscriber tibcoJMSTopicSubscriber = (TibcoJMSTopicSubscriber) Component.getInstance("tibcoJMSTopicSubscriber", ScopeType.STATELESS, true);
                javax.jms.Message message = tibcoJMSTopicSubscriber.subscribe();
                System.out.println("received message: " + message);
                if (message != null)
                {
      ...
      




      In the above scenario I get a CAST exception. I tried all different things, even moving the SLSB to a event context, and getting it from there. I see the bean in the Application Context (debug page), and it is defined with Scope STATELESS.
      As I said, when I use the @EJB it workd, when I use a POJO it works, when I use @In with a SLSB it gives me NULL.


      I read all the books,  and all say it is not a problem to use SLSB and inject them. Well not for me ;-). The project is a seam-gen, running on JBOSS 4.2.2. Latest seam version.


      Thanks all for your help.

        • 2. Re: EJB stateless bean injection
          Wolfgang Bergbauer Newbie

          Hi Norman,


          I tried. Same result, it does not work.
          One additional information. I tried to use this method of getting an instance via API with a POJO. This works, but as soon as I change the component tibcoJMSTopicSubscriber from POJO to EJB SLSB it fails again with a Cast. I checked the jboss console for the tibcoJMSTopicSubscriber  and it seems that there was never a creation of the stateless session bean (the create count is 0). I checked the JNDI name and that is correct:



          [org.jboss.ejb3.stateless.BaseStatelessProxyFactory] Binding proxy for TibcoJMSTopicSubscriber in JNDI at TicketSystem/TibcoJMSTopicSubscriber/local
          ...
          2009-03-30 23:29:02,831 INFO  [org.jboss.seam.Component] Component: tibcoJMSTopicSubscriber, scope: STATELESS, type: STATELESS_SESSION_BEAN, class: com.webea.session.TibcoJMSTopicSubscriber, JNDI: TicketSystem/TibcoJMSTopicSubscriber/local







          Code snippet
                  public String subscribe()
                  {       
                          
                          TibcoJMSTopicSubscriberIf tibcoJMSTopicSubscriber = (TibcoJMSTopicSubscriber) Component.getInstance("tibcoJMSTopicSubscriber", ScopeType.STATELESS, true);
                          javax.jms.Message message = tibcoJMSTopicSubscriber.subscribe();
          
          
          


                  

          • 3. Re: EJB stateless bean injection
            Arbi Sookazian Master

            you mean inject using the Local or Remote interface?  He is already doing that, no?


            @In(create=true)
            TibcoJMSTopicSubscriberIf tibcoJMSTopicSubscriber;



            where TibcoJMSTopicSubscriberIf is presumably the Local or Remote interface and tibcoJMSTopicSubscriber is the Seam component name.


            what's wrong?

            • 4. Re: EJB stateless bean injection
              Arbi Sookazian Master

              what version of Seam are you using?  I am using Seam 2.0.2-FP and was not able to reproduce the behavior you're seeing (i.e. it works fine when I inject SLSB into SFSB).


              xhtml:


              <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                                    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
              
              <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                   xmlns:ui="http://java.sun.com/jsf/facelets"
                   xmlns:h="http://java.sun.com/jsf/html"
                   xmlns:f="http://java.sun.com/jsf/core"
                   xmlns:a4j="http://richfaces.org/a4j"
                   xmlns:rich="http://richfaces.org/rich"
                   xmlns:s="http://jboss.com/products/seam/taglib"
                   template="/templates/normal.xhtml">
                   
              <ui:define name="body">
              <h:messages globalOnly="true" styleClass="message" />
              
                   <h:form>
                        <h:commandButton value="Go" action="#{testSFSBAction.myAction}"/>
                   </h:form>
                   
              </ui:define>     
              </ui:composition>



              SLSB:


              @Name("testPersistBean")
              @Stateless
              @AutoCreate
              public class TestPersistBean implements TestPersistLocal {
                       
                  @In 
                   private EntityManager entityManager;  //inject SMPC
                 
                  /*-------------------------------------------BEGIN METHODS---------------------------------------------*/
                   
                   public void persist(String serialNumber) 
                   {
                        TestTransactions testTransactions1 = new TestTransactions();
                        testTransactions1.setSerialNumber(serialNumber);
                        testTransactions1.setAddedDate(new Date());
                        entityManager.persist(testTransactions1);
                   
                        //entityManager.flush();  
                   }
              
              }



              SFSB:


              @Stateful
              @Name("testSFSBAction")
              public class TestSFSBAction implements TestSFSBLocal {
              
                   @In
                   private TestPersistLocal testPersistBean;
                   
                   @Logger
                   private Log log;
                   
                   /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&BEGIN METHODS&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
                        
                   public void myAction() {
                        log.info("begin myAction");
                        
                        testPersistBean.persist("12345");
                   }
                   
                   @Remove @Destroy
                   public void destroy() {}
              
              }



              server.log:


              13:37:57,762 INFO  [ProfilingInterceptor] *** Entering method: myAction
              13:37:57,762 INFO  [TestSFSBAction] begin myAction
              13:37:57,778 INFO  [ProfilingInterceptor] *** Entering method: persist
              13:37:57,809 INFO  [STDOUT] Hibernate: 
                  insert 
                  into
                      boBETS.dbo.TestTransactions
                      (addedDate, serialNumber) 
                  values
                      (?, ?)
              13:37:57,934 INFO  [ProfilingInterceptor] *** Method public void com.cox.bets.session.TestPersistBean.persist(java.lang.String) executed in 156ms ***
              13:37:57,934 INFO  [ProfilingInterceptor] *** Method public void com.cox.bets.session.TestSFSBAction.myAction() executed in 172ms ***
              13:37:58,199 INFO  [ProfilingInterceptor] *** Entering method: destroy
              13:37:58,199 INFO  [ProfilingInterceptor] *** Method public void com.cox.bets.session.TestSFSBAction.destroy() executed in 0ms ***

              • 5. Re: EJB stateless bean injection
                Arbi Sookazian Master

                you should be able to drop my files into your seam-gen'd project (adding the @Local interfaces of course) and just comment out this line in SFSB:


                testPersistBean.persist("12345");



                Bijection works this way: injection occurs in a sequential order (top to bottom) BEFORE your public method executes and outjection occurs after the method completes successfully (disinjection to set values null as appropriate happens in there somewhere as well).


                So the persist method is irrelevant as a POC for your problem.  If there is a problem injecting the SLSB into SFSB, you'll see some stack trace in the server.log.


                You will need this entity class as well (and if not, the SLSB example method does not necessarily need to persist any data to db, just log something).


                @Entity
                @Table(name = "TestTransactions")
                public class TestTransactions implements Serializable 
                {
                     private int testTransactionsId;     
                     private String serialNumber;
                     private Date addedDate;
                     
                     public TestTransactions() {
                     }
                
                     public TestTransactions(int testTransactionsId, String serialNumber) {
                          this.testTransactionsId = testTransactionsId;          
                          this.serialNumber = serialNumber;          
                     }
                     
                     @Id
                     @GeneratedValue(strategy=GenerationType.IDENTITY)
                     @Column(name = "TestTransactionsID", unique = true, nullable = false)
                     @NotNull
                     public int getTestTransactionsId() {
                          return this.testTransactionsId;
                     }
                
                     public void setTestTransactionsId(int testTransactionsId) {
                          this.testTransactionsId = testTransactionsId;
                     }
                
                     public String getSerialNumber() 
                     {
                          return serialNumber;
                     }
                
                     public void setSerialNumber(String serialNumber) 
                     {
                          this.serialNumber = serialNumber;
                     }
                
                     public Date getAddedDate() {
                          return addedDate;
                     }
                
                     public void setAddedDate(Date addedDate) {
                          this.addedDate = addedDate;
                     }
                     
                     
                }


                • 6. Re: EJB stateless bean injection
                  Stuart Douglas Master

                  You need to use the interface on the cast as well, not just the variable.

                  • 7. Re: EJB stateless bean injection
                    Wolfgang Bergbauer Newbie

                    Gentelmen, thanks for the great feedback. It finally works.
                    I have to apologize, I forgot the @Local on my interface definition.


                    The @In as well as the programmatic injection do work perfectly.


                    Just one more pragmatic question in regards to the injection method:


                    Since my SFSB is supporting a wizard, and at one specific step of the flow I need the SLSB to listen to an event, I used the API to inject the SLSB. Is this the right approach or would you say that the overhead of injecting with @In is neglectable? I assume that with each step of my wizard the @In would inject the SLSB even though the SLSB is not needed.


                    Wolfgang

                    • 8. Re: EJB stateless bean injection
                      Arbi Sookazian Master

                      @Local is default.


                      Seam dependency injection is dynamic, it occurs for all @In variables whenever a public exposed method is executed (unlike Spring, which injects on instantiation only).  Don't worry about it unless you see performance degradation.