≡ Menu

Smart Grids, part he: JNDI resource refs

In part 4 of the Smart Grids article, we created an Infinispan datagrid in JBoss and gave it a name. Our examples used that name to acquire the datagrid… which works, but isn’t really “right.”

We need to create a mechanism by which we can refer to a name local to our application code that refers to the global name.

In other words, we need to use JNDI correctly.

JNDI is the “Java Naming and Directory Interface.” You use it to look up objects bound to names in a given context. (It’s a lot like LDAP in this regard, and from what I can tell was heavily influenced by LDAP’s design and use.)

One of the most powerful aspects of JNDI for Java EE is that a name can be bound to another name in the context, so we can have one object bound to “my/sample/object“, create a reference called “my/local/object” and point it to my/sample/object, and use my/local/object to find the object.

Why is this useful?

Because it means we can have scoped references.

Imagine a service, imaginatively named “Service.” Service has a method call, let’s call it methodCall(). We are going to bind a reference to this Service object in JNDI, to “my/local/service“.

But wait! We need to change methodCall() to accept a parameter; requirements changed after initial deployment. The problem is that we have applications that depend on methodCall(), not methodCall(Object), so we can’t just change Service and redeploy.

This is where JNDI can help us.

Instead of binding Service 1.0 to my/local/service, suppose we deploy to my/local/service/1.0 instead. The updated service (with the parameter in methodCall()) would be deployed to my/local/service/2.0.

Our applications would use a locally scoped name of service, and that scope would point to the version of Service that the application requires. Thus, an application that relied on Service 1.0 would bind service to my/local/service/1.0, and an application that needed the new version would bind service to my/local/service/2.0.

This takes a bit of acclimation. We’re used to saying “Hey, I need something, let me go directly to it.” We open up URLs directly; we don’t use services that tell us where sports news might be, for example. We don’t set up a local DNS name for “mail” — we just point our mail program to whatever our mailserver’s domain actually is.

The difficulty with aliasing like this comes at deployment. We can (and will) set up an alias that connects a local name to the JNDI name we set up in the previous section, but since it’s part of our source tree, isn’t that the same as hardcoding the direct reference?

Honestly: it is the same as setting up a direct reference. What’s more, that’s actually the problem we’re trying to solve… by reproducing the problem in a different configuration file.

So why bother?

Well… we bother because even though we’re hardcoding the actual final name, we’re creating a clear placeholder for a deployer to correct if someone should want to change the reference.

The deployer is supposed to do this; in Java EE, a deployer is not only responsible for deploying the app but is also responsible for connecting local JNDI references to actual JNDI names.

So we create a configuration file that creates a resource reference (jboss-web.xml), and we expect that if this gets deployed in an environment where the actual canonical JNDI name for the Infinispan datagrid were to change, a deployer would modify this file, repackage the artifact, and deploy.


Wow. That’s a lot of explanation and very little actual demonstration. Let’s fix that.

The first thing we need to do is correct our reference names. We don’t want references that point to java:/jboss/infinispan/sensorData – we want names like java:comp/env/sensorData, instead. That means if we ever change the actual (canonical) name, our code doesn’t have to change.

That means our code selection from part four should look like this, instead:

public class DataCollector extends HttpServlet {
    @Resource(lookup = "java:comp/env/sensorData")
    private CacheContainer container;
    private Cache cache;

    @Override
    protected void doPost(HttpServletRequest request, 
                          HttpServletResponse response)
            throws ServletException, IOException {
        cache=container.getCache("dataPoints");
    ...

Well, that was easy. However, we can’t just refer to a name and expect it to exist, can we? We need to create a resource reference in web.xml that indicates that we have a locally scoped name of java:comp/env/sensorData, pointing to an object of type CacheContainer. Therefore, we add this to web.xml:


   sensorData
   org.infinispan.manager.CacheContainer

Now, this is great: we’ve got a reference named java:comp/env/sensorData, of the correct type… but it doesn’t point to anything. (Deployment should fail, actually, because the container should try to resolve all needed references, and this one won’t resolve yet.)

In JBoss, we can create a reference through the use of jboss-web.xml (for web applications, obviously.) We don’t have to do much in this file; all we need to do is indicate that the resource reference name points to an actual JNDI name, like so:

<?xml version="1.0" encoding="UTF-8"?>

<jboss-web xmlns="http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd">
   <resource-ref>
      <res-ref-name>sensorData</res-ref-name>
      <jndi-name>java:jboss/sensorData</jndi-name>
   </resource-ref>
</jboss-web>>

And now – after all this – we’re ready to start looking at our actual web application design.

Everything up to now has been infrastructure for our application; our next section will create some test data and display it.


Also see:

  1. The Role of JNDI in J2EE, an article I published on a similar topic for IBM DeveloperWorks, back in 2005