WildFly 8 introduced the EJB client API for managing remote EJB invocations. The EJB client API works off EJBClientContext(s). An EJBClientContext can potentially contain any number of EJB receivers. An EJB receiver is a component which knows how to communicate with a server which is capable of handling the EJB invocation. Typically EJB remote applications can be classified into:
- A remote client which runs as a standalone Java application
- A remote client which runs within another WildFly 8 instance
Depending on the kind of remote client, from an EJB client API point of view, there can potentially be more than 1 EJBClientContext(s) within a JVM.
In case of standalone applications, typically a single EJBClientContext (backed by any number of EJB receivers) exists. However this isn't mandatory. Certain standalone applications can potentially have more than one EJBClientContext(s) and a EJB client context selector will be responsible for returning the appropriate context.
In case of remote clients which run within another WildFly 8 instance, each deployed application will have a corresponding EJB client context. Whenever that application invokes on another EJB, the corresponding EJB client context will be used for finding the right EJB receiver and letting it handle the invocation.
In the Overview section we briefly looked at the different types of remote clients. Let's focus on the standalone remote clients (the ones that don't run within another WildFly 8 instance) for some of the next sections. Like mentioned earlier, typically a remote standalone client has just one EJB client context backed by any number of EJB receivers. Consider this example:
Now, we have seen in this other chapter EJB invocations from a remote client using JNDI that the JNDI lookups are (typically) backed by jboss-ejb-client.properties file which is used to setup the EJB client context and the EJB receivers. Let's assume we have a jboss-ejb-client.properties with the relevant receivers configurations. These configurations include the security credentials that will be used to create a EJB receiver which connects to the AS7 server. Now when the above code is invoked, the EJB client API looks for the EJB client context to pick a EJB receiver, to pass on the EJB invocation request. Since we just have a single EJB client context, that context is used by the above code to invoke the bean.
Now let's consider a case where the user application wants to invoke on the bean more than once, but wants to connect to the WildFly 8 server using different security credentials. Let's take a look at the following code:
So we have the same application, which wants to connect to the same server instance for invoking the EJB(s) hosted on that server, but wants to use two different credentials while connecting to the server. Remember, the client application has a single EJB client context which can have atmost 1 EJB recevier for each server instance. Which effectively means that the above code will end up using just one credential to connect to the server. So there was no easy way to have the above code working.
That was one of the use cases which prompted the https://issues.jboss.org/browse/EJBCLIENT-34 feature request. The proposal was to introduce a way, where you can have more control over the EJB client contexts and their association with JNDI contexts which are typically used for EJB invocations.
Developers familiar with earlier versions of JBoss AS would remember that for invoking an EJB, you would typically create a JNDI context passing it the PROVIDER_URL which would point to the target server. That way any invocation done on EJB proxies looked up using that JNDI context, would end up on that server. If we look back at the example above, we'll realize that, we are ultimately aiming for a similar functionality through https://issues.jboss.org/browse/EJBCLIENT-34. We want the user applications to have more control over which EJB receiver gets used for a specific invocation.
Before we introduced https://issues.jboss.org/browse/EJBCLIENT-34 feature, the EJB client context was typically scoped to the client application. As part of https://issues.jboss.org/browse/EJBCLIENT-34 we now allow the EJB client contexts to be scoped with the JNDI contexts. Consider the following example:
Notice any difference between this code and the earlier one? We now create and pass EJB client context specific properties to the JNDI context. So what do the EJB client context properties look like? The properties are the same that you would pass through the jboss-ejb-client.properties file, except for one additional property which is required to scope the EJB client context to the JNDI context. The name of the property is:
which is expected to have a value true. This property lets the EJB client API know that it has to created a EJB client context (backed by EJB receiver(s)) and that created context is then scoped/visible to only that JNDI context which created it. Lookup and invocation on any EJB proxies looked up using this JNDI context will only know of the EJB client context associated with this JNDI context. This effectively means that the other JNDI contexts which the application uses to lookup and invoke on EJBs will not know about the other scoped EJB client contexts at all.
JNDI contexts which aren't scoped to a EJB client context (for example, not passing the org.jboss.ejb.client.scoped.context property) will fallback to the default behaviour of using the "current" EJB client context which typically is the one tied to the entire application.
This scoping of the EJB client context helps the user applications to have more control over which JNDI context "talks to" which server and connects to that server in "what way". This gives the user applications the flexibility that was associated with the JNP based JNDI invocations prior to WildFly 8 versions.
|IMPORTANT: It is very important to remember that scoped EJB client contexts which are scoped to the JNDI contexts are NOT fire and forget kind of contexts. What that means is the application program which is using these contexts is solely responsible for managing their lifecycle and the application itself is responsible for closing the context at the right moment. After closing the context the proxies which are bound to this context are no longer valid and any invocation will throw an Exception. Not closing the context will end in resource problems as the underlying physical connection will stay open.
Read the rest of the sections in this article to understand more about the lifecycle management of such scoped contexts.
Like you saw in the previous sections, in case of scoped EJB client contexts, the EJB client context is tied to the JNDI context. It's very important to understand how the lifecycle of the EJB client context works in such cases. Especially since any EJB client context is almost always backed by connections to the server. Not managing the EJB client context lifecycle correctly can lead to connection leaks in some cases.
When you create a scoped EJB client context, the EJB client context connects to the server(s) listed in the JNDI properties. An internal implementation detail of this logic includes the ability of the EJB client context to cache connections based on certain internal algorithm it uses. The algorithm itself isn't publicly documented (yet) since the chances of it changing or even removal shouldn't really affect the client application and instead it's supposed to be transparent to the client application.
The connections thus created for a EJB client context are kept open as long as the EJB client context is open. This allows the EJB client context to be usable for EJB invocations. The connections associated with the EJB client context are closed when the EJB client context itself is closed.
|The connections that were manually added by the application to the EJB client context are not managed by the EJB client context. i.e. they won't be opened (obviously) nor closed by the EJB client API when the EJB client context is closed.|
The answer to that is simple. Use the close() method on the appropriate EJB client context.
The answer is the same, use the close() method on the EJB client context. But the real question is how do you get the relevant scoped EJB client context which is associated with a JNDI context. Before we get to that, it's important to understand how the ejb: JNDI namespace that's used for EJB lookups and how the JNDI context (typically the InitialContext that you see in the client code) are related. The JNDI API provided by Java language allows "URL context factory" to be registered in the JNDI framework (see this for details http://docs.oracle.com/javase/jndi/tutorial/provider/url/factory.html). Like that documentation states, the URL context factory can be used to resolve URL strings during JNDI lookup. That's what the ejb: prefix is when you do a remote EJB lookup. The ejb: URL string is backed by a URL context factory.
Internally, when a lookup happens for a ejb: URL string, a relevant javax.naming.Context is created for that ejb: lookup. Let's see some code for better understanding:
So we first create a JNDI context and then use it to lookup an EJB. The bean lookup using the ejb: JNDI name, although, is just one statement, involves a few more things under the hood. What's actually happening when you lookup that string is that a separate javax.naming.Context gets created for the ejb: URL string. This new javax.naming.Context is then used to lookup the rest of the string in that JNDI name.
Let's break up that one line into multiple statements to understand better:
As you see above, we split up that single statement into a couple of statements for explaining the details better. So as you can see when the ejb: URL string is parsed in a JNDI name, it gets hold of a javax.naming.Context instance. This instance is different from the one which was used to do the lookup (jndiCtx in this example). This is an important detail to understand (for reasons explained later). Now this returned instance is used to lookup the rest of the JNDI string ("app/module/distinct/bean!interface"), which then returns the EJB proxy. Irrespective of whether the lookup is done in a single statement or multiple parts, the code works the same. i.e. an instance of javax.naming.Context gets created for the ejb: URL string.
So why am I explaining all this when the section is titled "How to close scoped EJB client contexts"? The reason is because client applications dealing with scoped EJB client contexts which are associated with a JNDI context would expect the following code to close the associated EJB client context, but will be surprised that it won't:
Applications expect that the call to jndiCtx.close() will effectively close the EJB client context associated with the JNDI context. That doesn't happen because as explained previously, the javax.naming.Context backing the ejb: URL string is a different instance than the one the code is closing. The JNDI implementation in Java, only just closes the context on which the close was called. As a result, the other javax.naming.Context that backs the ejb: URL string is still not closed, which effectively means that the scoped EJB client context is not closed too which then ultimately means that the connection to the server(s) in the EJB client context are not closed too.
So now let's see how this can be done properly. We know that the ejb: URL string lookup returns us a javax.naming.Context. All we have to do is keep a reference to this instance and close it when we are done with the EJB invocations. So here's how it's going to look:
As you see, we changed the code to first do a lookup on just the "ejb:" string to get hold of the EJB naming context and then used that ejbRootNamingContext instance to lookup the rest of the EJB JNDI name to get hold of the EJB proxy. Then when it was time to close the context, we closed the ejbRootNamingContext (as well as the other JNDI context). Closing the ejbRootNamingContext ensures that the scoped EJB client context associated with that JNDI context is closed too. Effectively, this closes the connection(s) to the server(s) within that EJB client context.
If you are using that JNDI context only for EJB invocations, then yes you can get rid of some instances and code from the above code. You can change that code to:
Notice that we no longer hold a reference to 2 JNDI contexts and instead just keep track of the ejbRootNamingContext which is actually the root JNDI context for our "ejb:" URL string. Of course, this means that you can only use this context for EJB lookups or any other EJB related JNDI lookups. So it depends on your application and how it's coded.
Can't the scoped EJB client context be automatically closed by the EJB client API when the JNDI context is no longer in scope (i.e. on GC)?
That's one of the common questions that gets asked. No, the EJB client API can't take that decision. i.e. it cannot automatically go ahead and close the scoped EJB client context by itself when the associated JNDI context is eligible for GC. The reason is simple as illustrated by the following code:
As you can see, the doEJBInvocation() method first calls a lookupEJB() method which does a lookup of the bean using a JNDI context and then returns the bean (proxy). The doEJBInvocation() then uses that returned proxy and keeps doing the invocations on the bean. As you might have noticed, the JNDI context that was used for lookup (i.e. the ejbRootNamingContext) is eligible for GC. If the EJB client API had closed the scoped EJB client context associated with that JNDI context, when that JNDI context was garbage collected, then the subsequent EJB invocations on the returned EJB (proxy) would start failing in doEJBInvocation() since the EJB client context is no longer available.
That's the reason why the EJB client API doesn't automatically close the EJB client context.