|
||||||||||||||||
|
When resources such as DataSources and message queues are created and configured by the administrator, the JNDI name is one of the configurable properties. For the most part, the WebSphere administrator has the final say over the name a resource will use when binding itself to JNDI. A naming service uses a directory, or folder structure, to keep track of objects and their corresponding names. For example, EJBs usually go under a sub-directory named ejb, and database related objects usually go under a subdirectory named jdbc. An InitialContext is a connection to the root or base directory of the naming service, off of which all folders or subdirectories branch. To use an analogy, if the Windows operating system was a
directory server, the InitialContext would be the root, C:\, since all folders
and subdirectories sprout from there. When looking up an object bound to a JNDI server, we reference the name of the object relative to the root of the directory, or as we say in developerspeak, we reference the InitialContext. How does JNDI help with distributed programming? To obtain a reference to an EJB running on a remote server, we must first connect to the naming service on that server. Once we have a connection to the remote naming service, we provide the JNDI name of the desired EJB. If we provide the correct name, the naming service will return a reference to the EJB in question. How do I look up a JNDI addressable resource? When a connection pool is created, or an EJB is deployed, those resources are configured with a JNDI name. A DataSource might be bound to the naming service with the name jdbc/pulpds, or and EJB might be bound with the name ejb/com/pulpjava/session/Timer. To lookup a JNDI addressable resource in your application, your
Java code would look something like this:
How does a resource reference work? Of course, you would never, ever, want to hard code into your Java the name with which your EJB is bound to the naming service. There’s a whole whack of reasons why, not the least of which is the fact that the name you use to reference your EJB or DataSource at development time might be very different from the name the WebSphere Administrator gives to the resource at runtime. “Develop one way, deploy another, and use configurable bindings
to tie the two together.” What is so bad about hard coding JNDI names into your code? If you hardcode the names of JNDI addressable resources into your applications during the development phase, and the name of those resources happen to change at runtime, your applications will not work To avoid this problem, we use something called a resource reference to loosely bind the names used in our code to the actual name given to a J2EE resource at deployment time. At least, that’s what we should do. How does a resource reference work? Never hardcode a JNDI name in Java code. Code the name of a resource reference instead. For example, if an EJB is bound to the JNDI server with the name ejb/com/pulpjava/session/Timer, a resource reference with the name ejb/Timer could be configured to point to this EJB. In your code, you would perform a lookup against the name java:comp/env/ejb/Timer, which is the full name of the resource reference. The WebSphere environment recognizes anything under java:comp/env/ as a resource reference lookup. In this case, WebSphere will find a resource reference that binds the abstract name ejb/Timer with the actual ejb named ejb/com/pulpjava/session/Timer, and at runtime, a reference to the ejb/com/pulpjava/session/Timer will be returned to the client. If the actual name of the EJB running on the server ever changes, the application assembler would only be required to change the binding between the resource reference and the actual name of the EJB. No change to the code would need to be made. Resource references area great! What does a resource reference lookup look like? Resource references include the String “java:comp/env/” before the name of the configured reference name. To look up an ejb with the JNDI bound name of ejb/com/pulpjava/session/Timer, using a resource reference of ejb/Timer, your code would look something like this:
Notice how when using the resource reference, we use the prefix java:comp. The last line of code in this example looks up the Timer EJB according to the name used when binding the timer to the naming service. This is not a scalable or easily maintainable solution, and should never be done in production code. What does java:comp/env/ejb/Timer mean? Interpreting java:comp/env/ejb/Timer goes something like this: Using the connection to a JNDI server, connect to
the common Java namespace (java:) ejb/Timer (the
resource reference) F Return to the client a reference to the resolved resource Where are resource references stored and configured? A resource reference is an abstract binding configured in the deployment descriptor of the J2EE module of the component that uses it. For example, if a Servlet uses a resource reference, the deployment descriptor for the web module would contain the resource reference. If an EJB used a resource reference, the ejb-jar.xml file, which is the deployment descriptor for an EJB module, would contain the resource reference. When the application assembler prepares the application for deployment, they will configure the bindings and make sure the abstract name in the web or ejb module maps to the appropriate resource running on the server. After all, the name of the resource in the development environment will likely be different from the name of the resource in the pre-production (pre-prod) or testing environment. Again, develop one way, deploy another, and use configured bindings to tie the two together. Resource references make this happen. Isn’t configuring resource references and doing these bindings a lot of work? The extra configuration the administrator or application assembler must perform before deployment does constitute a bit of extra work, but it is not a huge burden. It sure beats having to dig into the code and fix all of the hard coded JNDI name references if the runtime name of a given resource ever changes. Besides, WebSphere Administrators never have much to do anyways – they like the extra work. How has naming changed between WebSphere 4 and WebSphere 5? In WebSphere 4, there was a central naming service to which all JNDI addressable resources in a common WebSphere domain were bound. With a single, centralized naming service, JNDI naming exceptions were few and far between. Regardless of which JVM your resource was running on, all you had to do was connect to the centralized naming service, which was accessible from any application server, provide the appropriate JNDI name or resource reference of the object in question, and Bob’s your uncle – the resource would be found. What were the drawbacks to a centralized JNDI service? Centralization of this supremely important J2EE service, called JNDI, certainly had its drawbacks. The biggest drawback to a centralized naming service was the fact that if this service went down, your entire enterprise environment was hosed. So what’s a J2EE server vendor like IBM to do? Should they have a centralized naming service that makes looking up objects easy, but also acts as a performance bottleneck, or should they have a distributed naming service that eliminates a single point of failure? IBM decided to pick their poison, and felt that having their developers chase down the occasional NameNotFoundException was far more acceptable than having a single point of failure in the WebSphere architecture. WebSphere 5 uses what is known as a distributed naming system. IBM has always been the vendor of failover and scalability, so it’s not surprising that their current implementation of the Java Naming and Directory Interface (JNDI) would lean in the direction of a solution that was more robust. How did we resolve remotely bound JNDI resources with WebSphere 4? Looking up Java components in the common namespace is how WebSphere applications have resolved JNDI names for the longest time. In fact, with prior versions of WebSphere, this common namespace was great, because all of the JVMs in a domain or cell shared the same namespace. If I had an EJB running on one node, and I had a Servlet clustered on a bunch of Java Virtual Machines on another machine somewhere else in the domain, all I had to do from my Servlet was connect to the local namespace, look up a resource reference using java:comp/env, and then the application server would take care of resolving the EJB to the appropriate JVM on the appropriate remote server. Resolving remote EJBs was a piece of cake with WebSphere 4. How do we lookup remote JNDI objects using WebSphere 5? WebSphere 5 introduced a brand new naming mechanism called “topology dependent JNDI names.” As was mentioned earlier, every Java Virtual Machine in your WebSphere 5 domain, also know as a WebSphere cell, has its own local JNDI namespace. While this local namespace is great at resolving other local objects that are bound to it, this local namespace will not, by default, resolve JNDI addressable objects bound to other application servers in the cell. This is a huge departure from the implementation of the naming service in WebSphere 4. To help facilitate resolving objects running on remote JVMs, WebSphere allows you to perform a JNDI lookup that includes both the name of the node and the name of the Java Virtual Machine on which the remote resource is running. To do this, you use a “topology dependent JNDI name.” What do topology dependent JNDI names look like? Imagine an administrative cell named “soccer” and a node in that administrative cell named ‘worldcup’ To find an EJB named “ejb/com/pulpjava/session/Timer” that is running on an application server named germany01 on the worldcup node, the topology dependent JNDI name used to perform the lookup would look like this: cell/nodes/worldcup/servers/germany01/ejb/com/pulpjava/session/Timer Notice how the JNDI name includes the name of the node, the server, and the EJB. The JNDI name is dependent upon the topology of the WebSphere environment. What is the advantage to using a topology dependent JNDI name? Using a topology dependent name, you will always be successful in locating a JNDI addressable resource. Since you are specifying explicitly the name of the resource, the JVM on which the resource is running, and the name of the node that manages the JVM, there is absolutely no ambiguity with regards to which resource you are searching. A topology dependent name will always resolve because the lookup is rerouted, through the local namespace, to the appropriate remote naming service. How do I reference a cluster when using a topology dependent name? Deploying an application across multiple JVMs is known as clustering. The WebSphere 5 naming infrastructure allows you to point to a cluster of servers, rather than a single instance of an application server. For an EJB named ejb/com/pulpjava/session/Timer, deployed to a cluster named england05, the lookup would be as follows: jndi.lookup(“cell/cluster/england05/ejb/com/pulpjava/session/Timer”); Notice that there is no mention of a server in the lookup, only the cluster. The lookup may resolve to the Timer EJB on any of the Java Virtual Machines that participate in the cluster. Should I use topology dependent names in my code? Topology dependent names will always resolve, but you’d have to be heavily inebriated to actually use them in your code. Anyone who places architecture specific references in what should be an architecturally neutral application deserves a good whooping. A topology dependent name uses both the name of the node, and the name of the server on which the J2EE resource is deployed. At development time, you have absolutely no idea what the nodes and production servers will be named - and even if you did, those names might change in the future. Using topology dependent names in your code is insane. Topology dependent JNDI names always work, but you shouldn’t use them. Then hat should you use? In the same manner that resource references can be set up in an enterprise application to tie a java:comp/env name to the name of an actual resource bound to the JNDI service, WebSphere 5 allows the WebSphere Administrators to create namespace bindings that binds a global, abstract, persistent, configurable JNDI name to a topology dependent name. Every JNDI server in a cell shares something called a persistent namespace. Using the deployment manager, an administrator can configure a JNDI name reference in this persistent namespace. This name and reference then gets passed to every node and server in the administrative cell. The configurable, persistent name can be used in JNDI lookups, and the application server will resolve these fixed, configurable names to the topology dependent name of the resource in question. Where are fixed or persistent JNDI names configured? A fixed, or persistent JNDI name, is configured by the WebSphere Administrator through the Administrative Console of the Deployment Manager. Unlike a resource-reference, a persistent JNDI name is not configured in a deployment descriptor. The binding of a fixed name to a topology dependent name becomes a persistent property of the WebSphere domain, and it is subsequently shared with every node and application server in the cell. Fixed, or persistent JNDI names, are part of the configuration of the WebSphere cell. In comparison, a resource reference is configured in the deployment descriptor of a web or EJB module. It is the job of the WebSphere administrator to go into the administrative console and map a persistent name to the topology dependent resource that is running on one of the application servers in the domain. How does a fixed JNDI name work? The following describes how a persistent JNDI name is mapped to a topology dependent JNDI name. F cell/cluster/england05/ejb/com/pulpjava/session/Timer is the topology dependent name of a clustered EJB F a persistent reference to this EJB is configured under the persistent namespace with the name ejb/Timer F cell/persistent/ejb/Timer is the name a developer uses in their client code when performing a JNDI lookup F the
application server resolves the persistent name to the topology dependent name,
and a reference to the EJB is returned to the client From any node in the cell, a lookup on the name cell/persistent/ejb/Timer would always resolve to a Timer running in the england05 cluster on the worldcup node. No EJB actually exists in this space called the ‘persistent namespace’ but instead, lookups performed on this persistent namespace get redirected to an actual living, breathing, J2EE resource deployed to a particular server on a particular node in the WebSphere domain. Will my WebSphere 4 applications work with the WebSphere 5, distributed namespace? Backwards compatibility? Don’t be a fool. In WebSphere 5, each JVM maintains its own local namespace. Objects created on that JVM are bound to the naming service running on that JVM. If your old applications did lookups on resources running on the same JVM, you should be okay. However, if your old applications used the common namespace to look up EJBs or other resources running on a different application server in the domain, then your applications will likely fail. When your component is in one JVM, and you are trying to resolve a resource that is bound to a JNDI server on another application server in the cell, a lookup on the local namespace will not work. If any of your old J2EE applications used the centralized naming service to look up remote objects, which is likely to be many, then they simply will not work. Again, in WebSphere 5, every JVM manages its own naming service. WebSphere 5 eliminates JNDI as a centralized, single point of failure, but it does introduce a huge NameNotFoundException headache when you are migrating your applications from version four to version five. How do we resolve NameNotFoundExceptions when migrating from version 4 to version 5? The good news is that a resource reference in a deployment descriptor of a WebSphere 4 application can be reconfigured to map to a fixed name of a resource running on a WebSphere 5 application server. If the resource reference is reconfigured to point at the fixed name, java:comp/env lookups will work, preserving backwards compatibility between WebSphere 4 and WebSphere 5 naming implementations. Name binding seems like a lot of work? Binding abstract names to architecturally specific names indeed creates more work for the WebSphere administrator, but it’s worth it. And in reality, it shouldn’t present a great deal of hardship for your administrative team. First of all, lookup names should have been abstracted out of applications, in the form of resource references, right from the start. Migrating older applications from one version of WebSphere to another should merely involve editing deployment descriptors, and perhaps the odd property file. Secondly, ‘code one way, deploy another’ should be a mantra, not a newsflash. Forcing developers to abstract out the names they use in their applications, and allowing administrators to bind a name used in code to a living, breathing, EJB running on a server, improves flexibility and maintainability. Creating a namespace binding is a little extra work, but it pays dividends. I’m just looking up my components using a relative name like ejb/User and everything works fine. If you’re using direct JNDI lookups, you’re walking on eggshells. Purely relative names such as ejb/User will always work if you’re accessing a resource on the local server; However, as soon as you break your application across multiple servers, clusters, or change the deployment name of your EJB, you’re entering the environment of ouch. When all of your EJBs and Servlets are running on the same server, which might occur in a testing environment, relative names will work. If you move that same application into a production environment, then you’ll be saying ‘Hola’ to the cleaning staff as you chase you’re your JNDI exceptions. Plan ahead and use a combination of resource references and the persistent namespace. What happens if my JNDI name or resource reference doesn’t resolve properly? If the naming service fails to resolve your JNDI lookup, you will enter the world of the NameNotFoundException, and in quite an unremarkable fashion, your application will ground to a halt. Don’t fret though – you’re not alone. I don’t know too many developers who haven’t dedicated a weekend or two to debugging naming issues on an application server. Here are a few JNDI troubleshooting hints: F Your name is case sensitive, and my name is case sensitive. Extend the same courtesy to your JNDI bound objects. JNDI names are case sensitive. F Try to stick with lower case letters when naming your EJBs, nodes and servers. One letter cased inconsistently will throw the lookup right off. F You should also notice that the name of the ejb is usually just the package name and the name of the home interface, all subfoldered under the ejb directory. F A common mistake is to do a lookup on the relative name “ejb/com.ibm.UserHome” The dot notation won’t work. Elements of the package name are delineated by Unix slashes, not periods. I’m still having trouble looking up my EJBs. Any pointers? Your best friend when having trouble looking up an EJB is the dumpnamespace utility. The dumpnamespace utility connects to the JNDI service running on your local sever and tells you exactly what is bound to the local namespace. Dumpnamespace can provide information about objects bound to the persistent namespace as well. You can even provide an ip-address as an argument and connect to remote JNDI servers. Always be careful which sever and port to which you’re connecting when using a dumpnamespace command. A typical dumpnamespace will go against port 2809 or 9809. Depending on how you’ve configured compatibility, your port number might vary by a digit here or there. Good luck. Chasing down NameNotFoundExceptions is never fun, but with patience and persistence, you’ll crack the naming nut that’s bothering you.
|
| |
|||||||||||||
|
||||||||||||||||