12

I have Java EE 5 project using JBoss 5.1 and problem like this. I have to do kind of run-time lookup for some EJBs in MDBs using a string that results from message content. It's just kind of service locator pattern used in MDBs. Now, since MDBs start consuming just after deploy, I have a lot NameNotFoundException since implicit deployment order doesn't work well here (run-time lookup). What do you think about it? Is it possible to do it really well using EJB 3.0? It's also acceptable for me to use any vendor-specific stuff (JBoss 5.1) if it resolves the problem.

Some code snippet to visualize the situation:

@MessageDriven(mappedName="jms/Queue")
public class MessageBean implements MessageListener {

    @Resource
    private MessageDrivenContext mdc;

    public void onMessage(Message msg) {

        final String beanName = // extract somehow the bean's name from 'msg'
        final Context ctx = new InitialContext();
        final Object obj = ctx.lookup(beanName); // NameNotFoundException
        // do something with 'obj'
    }
}
Michał Kalinowski
  • 14,509
  • 4
  • 31
  • 44
  • Quick idea: If you have transactionalized the consumption, you could perhaps reject the tx on `NameNotFoundException`'s, have a sensible retry-policy and hope the EJBs has deployed next time? – Anders R. Bystrup Feb 20 '13 at 11:41
  • It's something, but notice that if I'll have milions of messages waiting in queues (that's my case actually), every one will be initially processed to just reject tx. The database will probably kneel. It anyway gets a huge load on application startup. – Michał Kalinowski Feb 21 '13 at 08:10
  • Can you paste a snippet of code here .. how are performing lookup . whats your MDB class definition ? – user1428716 Feb 21 '13 at 17:47
  • can you just not persist the messages in queue, so that after deploy the queue would be empty? given your case, I don't understand why you're persisting such messages. – eis Feb 22 '13 at 07:34
  • Unfortunately, I have to. It's kind of financial processing, we really have to persist all messages (and even replicate them but that's another story). – Michał Kalinowski Feb 22 '13 at 12:33

4 Answers4

4

Use one of these four different approaches.

  1. Declare EJB dependencies (EJB references) using "@EJB" annotation (don't use JNDI lookup). For entity bean references, must refer to the entity bean home interface. Container must ensure all dependencies are injected before methods/message-listeners are processed:

    MessageDriven(mappedName="jms/Queue")
    public class MessageBean implements MessageListener {

    @EJB private EntityBeanHomeA entityBeanHomeA;    
    
    @EJB private EntityBeanHomeB entityBeanHomeB;    
    
    @EJB private EntityBeanHomeC entityBeanHomeC;    
    
    @EJB private SessionBeanD sessionBeanD;    
    
    @Resource
    private MessageDrivenContext mdc;
    
    public void onMessage(Message msg) {
    
        final String beanName = // extract somehow the bean's name from 'msg'
        final Object obj = getDependentEJB(beanName);
        // do something with 'obj'
    }
    
    private Object getDependentEJB(String beanName) {
        Object result = null;
        if ("EntityBeanHomeA".equals(beanName)) {
             result = entityBeanHomeA;
        else if ("EntityBeanHomeB".equals(beanName)) {
             result = entityBeanHomeB;
        else ("EntityBeanHomeC".equals(beanName)) {
             result = entityBeanHomeC;
        else ("SessionBeanD".equals(beanName)) {
             result = sessionBeanD;
        }
        return result;
    }
    

    }

  2. Use JNDI lookup, but declare EJB dependencies via EJB deployment descriptors. Again, the container must ensure ensure all dependencies are setup before methods/messages are processed:

    @MessageDriven(mappedName="jms/Queue") public class MessageBean implements MessageListener {

    // as given in the original Question...
    

    }

    Deployment descriptor:

    <enterprise-beans>
        <message-driven>
            ... 
            <ejb-name>MessageBean</ejb-name>
            <ejb-class>com.company.pkg.MessageBean</ejb-class> 
            <messaging-type>javax.jms.MessageListener</messaging-type>
            <message-destination-type>javax.jms.Queue</message-destination-type>
            <message-destination-link>ExpenseProcessingQueue</message-destination-link>
            <ejb-ref> 
                <description> This is a reference to an EJB 2.1 entity bean
                  that encapsulates access to employee records. 
                </description>
                <ejb-ref-name>ejb/EmplRecord</ejb-ref-name>
                <ejb-ref-type>Entity</ejb-ref-type>
                <home>com.wombat.empl.EmployeeRecordHome</home>
                <remote>com.wombat.empl.EmployeeRecord</remote> 
                <ejb-link>EmployeeRecord</ejb-link> <-- if in same EJB jar -->
                          <-- ../emp/emp.jar#EmployeeRecord   if in diff EJB jar -->
            </ejb-ref>
            <ejb-local-ref> 
                <description> This is a reference to the local business interface
                   of an EJB 3.0 session bean that provides a payroll service. 
                </description> 
                <ejb-ref-name>ejb/Payroll</ejb-ref-name>
                <local>com.aardvark.payroll.Payroll</local> 
                <ejb-link>Payroll</ejb-link> 
            </ejb-local-ref>
            <ejb-local-ref> 
                <description> This is a reference to the local business interface of an
                  EJB 3.0 session bean that provides a pension plan service. 
                </description>
                <ejb-ref-name>ejb/PensionPlan</ejb-ref-name>
                <local>com.wombat.empl.PensionPlan</local> 
                <ejb-link>PensionPlan</ejb-link> <-- if in same EJB jar -->
            </ejb-local-ref> 
            ...
        </message-driven>
        ... 
    </enterprise-beans>
    
  3. Use JNDI lookup but do not declare dependencies using either @EJB annotations or EJB deployment - handle entirely with your own logic, without the container helping. Use delays/error handling.

  4. Use JBoss proprietary configuration to control deployment order:

    http://texnoblog.wordpress.com/2010/09/16/depends-in-jboss/

    How to order deployment of EJBs and JMS queue config in JBoss 5?

Community
  • 1
  • 1
Glen Best
  • 21,413
  • 2
  • 52
  • 72
  • Glen, thanks for the answer. That confirms that there's no any strict and elegant solution using these technologies. Kind of hacking is needed anyway. I'll probably try with 3 or 4 option. – Michał Kalinowski Feb 27 '13 at 08:43
2

One way would be to create a dummy-ejb that you inject into your MDB, this way your mdb would not start consuming until that injection can actually take place.

If the dummy-ejb is bundled with the EJBs you intended to do dynamic lookup on, this should work

My answer here solves a similiar use-case.

Community
  • 1
  • 1
Aksel Willgert
  • 10,807
  • 5
  • 48
  • 70
1

I think that the best approach would be to delay the deployment of your MDB until all your EJBs are up & running. This is basically approach number 4 in the answer above.

"Use JBoss proprietary configuration to control deployment order:

http://texnoblog.wordpress.com/2010/09/16/depends-in-jboss/

How to order deployment of EJBs and JMS queue config in JBoss 5?"

Community
  • 1
  • 1
Doron Manor
  • 536
  • 3
  • 5
1

you could implement a loop with backoff around the lookup call.

jtahlborn
  • 50,774
  • 5
  • 71
  • 112