There is a plethora of frustratingly incorrect (better description - "close but no cigar") information concerning remote access to JBoss EJBs from a standalone application. I've been beating my head against this wall for over a day with no success.
I'm trying to port an EJB from WebLogic to JBoss, which is called by a standalone application running on another server.
I've been here, here, and several other places chasing down various "solutions" to my problem without success. I've tried reading the official documentation which wants me to install a "quickstart" based on Maven, which may or may not fit my situation, and which so far I have decided not to pursue. (My project is not built with Maven, it uses Gradle, but I am reasonably certain that I've managed to get all the right dependencies deployed).
I have a stateful EJB deployed in a WAR inside an EAR (previous implementation of deploying it simply in a WAR did not help matters).
I configure the client thusly:
public InitialContext createInitialContext() throws NamingException {
Properties prop = new Properties();
prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
prop.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.naming.remote.client.InitialContextFactory");
prop.put(Context.PROVIDER_URL, purl);
prop.put(Context.SECURITY_PRINCIPAL, "myusername");
prop.put(Context.SECURITY_CREDENTIALS, "mypassword");
prop.put("jboss.naming.client.ejb.context", false);
return new InitialContext(prop);
}
public void closeContext(Context context) throws NamingException {
if (context != null) {
context.close();
}
}
private String getJndiName(
String prefix,
String appName,
String moduleName,
String distinctName,
String beanName,
Class viewClass,
boolean stateful)
{
StringBuilder builder = new StringBuilder();
if (prefix != null && prefix.length() > 0) {
builder.append(prefix).append(':');
}
builder.append(appName)
.append('/')
.append(moduleName)
.append('/')
.append(distinctName)
.append('/')
.append(beanName).append('!')
.append(viewClass.getName());
if (stateful) {
builder.append("?stateful");
}
return builder.toString();
}
public Object lookup(Context context) throws NamingException {
final String prefix = "ejb";
final String appName = "myearname";
final String moduleName = "mywarname";
final String distinctName = "";
final String beanName = "MyBean";
final Class viewClass = MyBeanInterface.class;
String jndi = getJndiName(prefix, appName, moduleName, distinctName, beanName, viewClass, true);
return context.lookup(jndi);
}
Note that no "distinct name" is provided as none is needed. "distinct name" is supposed to be optional: All of this gets invoked by:
MyBeanInterface sstatus = null;
try {
ctx = createInitialContext();
sstatus = (MyBeanInterface) lookup(ctx);
} catch (Exception ex) {
...
}
When this code is invoked, the following error message is produced:
Caused by: java.lang.IllegalStateException: EJBCLIENT000024: No EJB receiver available for handling [appName:SockTransport, moduleName:SockTransport, distinctName:] combination
at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:873) ~[ttjd.jar:?]
at org.jboss.ejb.client.EJBClient.createSessionWithPossibleRetries(EJBClient.java:222) ~[ttjd.jar:?]
at org.jboss.ejb.client.EJBClient.createSession(EJBClient.java:202) ~[ttjd.jar:?]
at org.jboss.ejb.client.naming.ejb.EjbNamingContext.doCreateProxy(EjbNamingContext.java:227) ~[ttjd.jar:?]
at org.jboss.ejb.client.naming.ejb.EjbNamingContext.createEjbProxy(EjbNamingContext.java:204) ~[ttjd.jar:?]
Using the above code, the JNDI name I am supplying is
ejb:myearname/mywarname//MyBean!com.whatever.my.package.MyBeanInterface
. Note the double slash caused by the missing distinctName. I can and have rejiggered this code to produce instead ejb:myearname/mywarname/MyBean!com.whatever.my.package.MyBeanInterface
and this makes no difference.
Frankly, I think this error message is a red herring. I suspect that there is some other problem with my setup that is not being caught and breaking on this interface. I don't think the distinct name or lack thereof has anything to do with the problem. I think that's simply how they log the object that can't be looked up.
Before I go down the path of figuring out how to add a useless "distinct name" in a probably vain attempt to keep JBOSS happy, can someone venture a guess as to what the real problem may be?
UPDATE:
The suggestions of @Steve_C are quite illuminating but I still have not gotten them to work. He left a few points out of the initial context creation:
- Context.URL_PKG_PREFIXES
- Context.INITIAL_CONTEXT_FACTORY
- "jboss.naming.client.ejb.context"
but these were mentioned in the resource he cited - very handy by the way.
So I added these and my createInitialContext method now looks like this:
public InitialContext createInitialContext() throws NamingException {
Properties prop = new Properties();
prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
prop.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.naming.remote.client.InitialContextFactory");
prop.put(Context.PROVIDER_URL, "http-remoting://{server-ip}:{server-port});
prop.put("jboss.naming.client.ejb.context", true);
return new InitialContext(prop);
}
Why PROVIDER_URL is necessary when I've already supplied server-ip and server-port in the jboss-ejb-client.properties file remains mysterious, but it makes a difference.
With these three items added to my initial context environment, now I get a different error message (EJBCLIENT000025 instead of EJBCLIENT000024):
java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:SockTransport, moduleName:SockTransport, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@67f639d3
at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:798) ~[ttjd.jar:?]
at org.jboss.ejb.client.ReceiverInterceptor.handleInvocation(ReceiverInterceptor.java:128) ~[ttjd.jar:?]
at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186) ~[ttjd.jar:?]
at org.jboss.ejb.client.EJBInvocationHandler.sendRequestWithPossibleRetries(EJBInvocationHandler.java:255) ~[ttjd.jar:?]
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:200) ~[ttjd.jar:?]
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:183) ~[ttjd.jar:?]
at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:146) ~[ttjd.jar:?]
at com.sun.proxy.$Proxy20.create(Unknown Source) ~[?:?]
I suppose this counts as progress, but I'm finding this more difficult than it needs to be. I wonder if these new properties need to be in the properties file, but the official documentation pretty clearly says they don't.