The JNDI lookup for the JTA UserTransaction is not available to MBean threads in Websphere Application Server 7

Joseph MacFarlane picture Joseph MacFarlane · Dec 1, 2011 · Viewed 16.4k times · Source

I'm trying to invoke business logic via JMX (using 'standard' MBeans) in a web application in Websphere Application Server 7 with JTA switched on and would like to know why this business logic can't see the JTA UserTransaction when invoked from an MBean (because it can when invoked via the web app's UI).

When hibernate attempts to look up the UserTransaction via 'java:comp/UserTransaction', the following exception is thrown:

org.hibernate.TransactionException: Could not find UserTransaction in JNDI [java:comp/UserTransaction]
    at org.hibernate.transaction.JTATransactionFactory.getUserTransaction(JTATransactionFactory.java:173)
    at org.hibernate.transaction.JTATransactionFactory.createTransaction(JTATransactionFactory.java:149)

    ...

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:48)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
    at java.lang.reflect.Method.invoke(Method.java:600)
    at com.sun.jmx.mbeanserver.StandardMBeanIntrospector.invokeM2(StandardMBeanIntrospector.java:105)
    at com.sun.jmx.mbeanserver.StandardMBeanIntrospector.invokeM2(StandardMBeanIntrospector.java:39)
    at com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(MBeanIntrospector.java:220)
    at com.sun.jmx.mbeanserver.PerInterface.getAttribute(PerInterface.java:77)
    at com.sun.jmx.mbeanserver.MBeanSupport.getAttribute(MBeanSupport.java:228)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getAttribute(DefaultMBeanServerInterceptor.java:678)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.getAttribute(JmxMBeanServer.java:650)
    at com.ibm.ws.management.AdminServiceImpl.getAttribute(AdminServiceImpl.java:853)
    at com.ibm.ws.management.remote.AdminServiceForwarder.getAttribute(AdminServiceForwarder.java:270)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1415)
    at javax.management.remote.rmi.RMIConnectionImpl.access$200(RMIConnectionImpl.java:84)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1276)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1371)
    at javax.management.remote.rmi.RMIConnectionImpl.getAttribute(RMIConnectionImpl.java:612)
    at javax.management.remote.rmi._RMIConnectionImpl_Tie.getAttribute(_RMIConnectionImpl_Tie.java:578)
    at javax.management.remote.rmi._RMIConnectionImpl_Tie._invoke(_RMIConnectionImpl_Tie.java:98)
    at com.ibm.CORBA.iiop.ServerDelegate.dispatchInvokeHandler(ServerDelegate.java:622)
    at com.ibm.CORBA.iiop.ServerDelegate.dispatch(ServerDelegate.java:475)
    at com.ibm.rmi.iiop.ORB.process(ORB.java:513)
    at com.ibm.CORBA.iiop.ORB.process(ORB.java:1574)
    at com.ibm.rmi.iiop.Connection.respondTo(Connection.java:2841)
    at com.ibm.rmi.iiop.Connection.doWork(Connection.java:2714)
    at com.ibm.rmi.iiop.WorkUnitImpl.doWork(WorkUnitImpl.java:63)
    at com.ibm.ejs.oa.pool.PooledThread.run(ThreadPool.java:118)
    at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1563)
Caused by: javax.naming.ConfigurationException: A JNDI operation on a "java:" name cannot be completed because the server runtime is not able to associate the operation's thread with any J2EE application component.  This condition can occur when the JNDI client using the "java:" name is not executed on the thread of a server application request.  Make sure that a J2EE application does not execute JNDI operations on "java:" names within static code blocks or in threads created by that J2EE application.  Such code does not necessarily run on the thread of a server application request and therefore is not supported by JNDI operations on "java:" names. [Root exception is javax.naming.NameNotFoundException: Name "comp/UserTransaction" not found in context "java:".]
    at com.ibm.ws.naming.java.javaURLContextImpl.throwConfigurationExceptionWithDefaultJavaNS(javaURLContextImpl.java:428)
    at com.ibm.ws.naming.java.javaURLContextImpl.lookup(javaURLContextImpl.java:399)
    at com.ibm.ws.naming.java.javaURLContextRoot.lookup(javaURLContextRoot.java:214)
    at com.ibm.ws.naming.java.javaURLContextRoot.lookup(javaURLContextRoot.java:154)
    at javax.naming.InitialContext.lookup(InitialContext.java:455)
    at org.hibernate.transaction.JTATransactionFactory.getUserTransaction(JTATransactionFactory.java:163)
    ... 53 more
Caused by: javax.naming.NameNotFoundException: Name "comp/UserTransaction" not found in context "java:".
    at com.ibm.ws.naming.ipbase.NameSpace.lookupInternal(NameSpace.java:1178)
    at com.ibm.ws.naming.ipbase.NameSpace.lookup(NameSpace.java:1095)
    at com.ibm.ws.naming.urlbase.UrlContextImpl.lookup(UrlContextImpl.java:1233)
    at com.ibm.ws.naming.java.javaURLContextImpl.lookup(javaURLContextImpl.java:395)
    ... 57 more

This problem looks like it's more than just a hibernate configuration problem - hibernate is looking for the UserTransaction at what IBM say is the correct UserTransaction JNDI location ('java:comp/UserTransaction') - see this infocenter document.

Furthermore, I can reproduce the problem in a simple web app that has an MBean that does the lookup:

public class JTALookup extends NotificationBroadcasterSupport implements JTALookupMBean {
  Log log = LogFactory.getLog(JTALookup.class);

  /**
   * {@inheritDoc}
   * @see JTALookupMBean#lookupUserTransaction()
   */
  @Override
  public void lookupUserTransaction() {
    try {
      log.info("Attempting 'java:comp/UserTransaction' lookup");
      Object usrTxn = new InitialContext().lookup("java:comp/UserTransaction");
      log.info("Successfully looked up 'java:comp/UserTransaction' [" + usrTxn + "]." );
    } catch (NamingException e) {
      log.info("'java:comp/UserTransaction' lookup failed");
      throw new RuntimeException("Failed to lookup JTA user transaction", e);
    }
  }

and a context listener that invokes the lookup during start up and then registers the MBean:

public void contextInitialized(ServletContextEvent sce) {

    log.info("Initialising context");

    JTALookup jtaLookup = new JTALookup();
    jtaLookup.lookupUserTransaction(); // This succeeds
    log.info("Looked up JTA transaction");

    MBeanServer mbServer = AdminServiceFactory.getMBeanFactory().getMBeanServer();
    log.info("Got MBeanServer");

    try {
      mbServer.registerMBean(jtaLookup, new ObjectName("webJTALookupStub:type=JTALookup"));
      log.info("Registered dummy MBean");
    } catch (Exception e) {
      log.info("Failed to register dummy MBean");
      throw new RuntimeException("Failed to register dummy MBean", e);
    }
}

The lookup on 'java:comp/UserTransaction' succeeds during context initialisation, but fails (with a similar stack trace to that above) when invoked via jmx, like so:

public static void main(String[] args) {

    JMXServiceURL url = new JMXServiceURL(
        "service:jmx:rmi://" + "your.server.name.co.uk" + ":" + "2809" + "/jndi/JMXConnector"
    );

    Hashtable<String, Object> env = new Hashtable<String, Object>();
    env.put(Context.PROVIDER_URL, "corbaloc:iiop:gbbldd66.sys.chp.co.uk:2809/WsnAdminNameService");
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");

    // Establish the JMX connection.
    JMXConnector jmxc = JMXConnectorFactory.connect(url, env);

    // Get the MBean server connection instance.
    MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

    ObjectName mbeanName = new ObjectName("webJTALookupStub:type=JTALookup");

    JTALookupMBean mBean = JMX.newMBeanProxy(mbsc, mbeanName, JTALookupMBean.class, true);

    mBean.lookupUserTransaction(); // This fails

The 'Extending the WebSphere Application Server administrative system with custom MBeans' document in IBM's infocenter suggests that standard MBeans that have been tested in applications outside WAS should just work.

IBM do state that the UserTransaction lookup is not available to:

  • CMT Enterprise beans `http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.base.doc/info/aes/ae/cjta_glotran.html

  • Async Beans created by EJBs `http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/com/ibm/websphere/asynchbeans/package-summary.html?resultof=%22%61%73%79%6e%63%68%62%65%61%6e%22%20%22%75%73%65%72%74%72%61%6e%73%61%63%74%69%6f%6e%22%20%22%75%73%65%72%74%72%61%6e%73%61%63%74%22%20

Apologies for the non-functional links - I'm a new user and so can only post two working links.

Do plain old MBeans fall into either of these categories from IBM's point of view?

Interestingly, the UserTransaction appears to be available on JNDI lookup 'jta/UserTransaction' and using that as a fallback option seems to work - but:

  • WAS 7 is Java EE 5 compliant and as of J2EE 1.3 'java:comp/UserTransaction' is the specified JNDI location for the UserTransaction - see the J2EE 1.3 spec `http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf

  • Using a lookup from an earlier version of the EE specification seems like a potential source of other bugs, and might only be addressing part of my problem - the fact that WAS thinks my MBean's thread is not associated with an application could cause other issues.

One further point to note is that the UserTransaction is also hidden to threads for work submitted from the MBean to the application's work manager (an IBM work manager) - which could be because it's treating that work like it's an async bean submitted by an EJB?

Possible explanations that have occurred to me are:

  • There might issues with how IBM set up MBean threads in WAS 7 and associate then with the applications that register the MBeans.

  • There might be some additional configuration options for the MBean registration that would let WAS know that it should associate the MBean with the application that registered it. I have tried several alternative approaches but saw the same exception each time:

    • Registering the MBeans with UserCollaborators and xml descriptors

    • Registering them with ModelMBeanInfo

    • Registering them with the AdminService rather than the MBeanServer

    • Enhancing the ObjectName for the MBean with additional properties (Application, J2EEApplication) at registration

  • There might be some additional configuration options for the jmx client request that would let WAS know that it should associate the MBean invokation with the appropriate application. This forum post suggests it's possible to configure a client application to have access to the initial context: `http://www.ibm.com/developerworks/forums/thread.jspa?messageID=14021995

  • I might just not be supposed to try to use MBeans this way - despite IBM's statements that I should be able to. It's been suggested that EJBs are the appropriate solution for this kind of requirement.

Any light that can be shed on this problem would be greatly appreciated.

Answer

shelley picture shelley · Dec 1, 2011

MBeans run on a separate thread than your application, so they don't have access to the application naming context in JNDI, and therefore they don't have access to your UserTransaction.

I think your final potential explanation is likely most accurate:

I might just not be supposed to try to use MBeans this way - despite IBM's statements that I should be able to. It's been suggested that EJBs are the appropriate solution for this kind of requirement.

MBeans may not be appropriate for this type of work. Rather, using EJBs or a web service might be more appropriate.