I am trying to invoke a stateless EJB, deployed on a remote server. I can invoke the bean from my local JBoss environment but when I change the remote.connection.default.host to the remote machine's host, my client code does not work.
This is my jboss-ejb-client.properties:
endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=SERVERIP/HOSTNAME
remote.connection.default.port=8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=username
remote.connection.default.password=Password
And my client code looks like this:
Properties properties = new Properties();
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
String jndi = "jndi_name";
Context context = new InitialContext(properties);
obj = context.lookup(jndi);
Please help.
Thanks all.
Jack.
This answer may be late but I faced the same problem, none of the above answers helped me, to solve this problem, refer the following : http://blog.jonasbandi.net/2013/08/jboss-remote-ejb-invocation-unexpected.html
The code that works for me is as below:
Properties jndiProperties=new Properties();
jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
jndiProperties.put(Context.PROVIDER_URL, "http-remoting://127.0.0.1:8080/");
//This property is important for remote resolving
jndiProperties.put("jboss.naming.client.ejb.context", true);
//This propert is not important for remote resolving
jndiProperties.put("org.jboss.ejb.client.scoped.context", "true");
Context context=new InitialContext(jndiProperties);
/*
java:global/JEETest_Project/EJBTest_Project/GenericStateless!test.stateless.GenericStateless
java:app/EJBTest_Project/GenericStateless!test.stateless.GenericStateless
java:module/GenericStateless!test.stateless.GenericStateless
java:jboss/exported/JEETest_Project/EJBTest_Project/GenericStateless!test.stateless.GenericStateless
java:global/JEETest_Project/EJBTest_Project/GenericStateless
java:app/EJBTest_Project/GenericStateless
java:module/GenericStateless
*/
//None of the above names work for remote EJb resolution ONLY THIS WORKS -
//"/JEETest_Project/EJBTest_Project/GenericStateless!test.stateless.GenericStateless"
GenericStateless bean=(GenericStateless)context.lookup("/JEETest_Project/EJBTest_Project/GenericStateless!test.stateless.GenericStateless");
//GenericStateless bean=(GenericStateless)c.lookup("GenericStateless!test.stateless.GenericStateless");
System.out.println(bean.getInt());
Foolishly believing that I understood EJB deployment on Wildfly, I struggled with remote invocation of my stateless EJBs from a Servlet for several days, trying various configurations from snippets of code that I found on the net. No luck.
I finally came across the following guide from JBOSS which had my problems resolved in about 10 minutes. I had incorrectly configured my remote outbound connection. The Wildfly developers have written an awesome Java EE application server, but they are terrible with explanatory error messages.
The guide is for AS7.2 but it was relevant for my Wildfly 8.2.0 platform.
Hopefully this will save someone the grief that I suffered.
What worked for me:
My client was in a standalone maven project. All I needed to do was to add this dependency:
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>10.1.0.Final</version>
<type>pom</type>
<scope>runtime</scope>
</dependency>
I've come to this solution by looking at the ejb-remote example.
Your client code has to use EJB 3 standardized JNDI name to access your beans
Properties properties = new Properties();
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
// Not needed but I like to see it when debugging
properties.put(Context.PROVIDER_URL, "http-remoting://localhost:8080/");
// EJB 3 bean naming convention
String jndi = "ejb:/myapp/mymodule/myEjbName!my.own.package.EjbInterface";
Context context = new InitialContext(properties);
obj = context.lookup(jndi);
And check WildFly server.log for the exported JNDI binding of your bean: java:jboss/exported/myapp/mymodule/myEjbName!my.own.package.EjbInterface
To pass security checks, username must be defined in ApplicationRealm.
Usually in Java EE, you let the container do the lookup instead of doing it yourself. From the Java EE 6 tutorial:
To obtain a reference to the remote business interface of an
enterprise bean through dependency injection, use the javax.ejb.EJB
annotation and specify the enterprise bean’s remote business interface
name:
#EJB Example example;
If it has to be JNDI lookup, check out this JBoss tutorial - it's for version 7 though, but maybe it is of help for your case.
Related
I'm working with an existing web application (running in Tomcat 7.0.26) that normally communicates with external systems over IBM WebsphereMQ, via the JMS API. The code looks similar to:
Hashtable<String, String> env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
env.put(Context.PROVIDER_URL, providerURL);
// Note that this is instantiating an InitialDirContext,
// NOT an InitialContext!!!
Context context = new InitialDirContext(environment);
qcf = (QueueConnectionFactory)context.lookup(qcfName);
requestQueue = (Queue)context.lookup(requestQueueName);
responseQueue = (Queue)context.lookup(responseQueueName);
//... etc, usual type of JMS code
I'm now trying to make this work with ActiveMQ 5.10 client libraries. A restriction on my end is that I cannot modify the source code. The only thing I have to play with are the variables as shown in the above code:
initialContextFactory is in the current case always set to com.sun.jndi.fscontext.RefFSContextFactory
providerURL is currently set to a file://C:/directory URL, pointing to a .bindings file as generated by "Managed JMS Objects" from WebsphereMQ Explorer
requestQueueName & responseQueueName
I'm now changing initialContextFactory to org.apache.activemq.jndi.ActiveMQInitialContextFactory, and providerURL to tcp://localhost:61616. Although I'm not getting any errors from the code, it doesn't work. I cannot see any connection in the ActiveMQ Web Admin console.
Researching on the web, I see that normally ActiveMQ JNDI with Tomcat is set up differently:
Resource entries in context.xml
Create the InitialContext as new InitialContext();
get a 'sub context' as envContext = (Context) initCtx.lookup("java:comp/env");
Get the JMS objects from this sub context (using a "jms/" prefix)
But as I wrote before, this is not an option for me: although I have access to the code, I cannot modify it.
Does anybody know a way around this? How can I get ActiveMQ QueueConnectionFactory/Queue objects from an instance of InitialDirContext, initialized only with INITIAL_CONTEXT_FACTORY and PROVIDER_URL, and presumably without any additional configuration on the Tomcat side (although if necessary I do have the option of changing the tomcat config as well).
Maarten
Turns out I needed to include geronimo-j2ee-management_1.1_spec-1.0.1.jar into the CLASSPATH of this application as well. Without this, an exception was thrown that wasn't logged anywhere, so it took some ingenuity to figure that one out.
Maarten
I am trying to do a lookup from one deployment to another deployment, using exactle the same bean implementations. It is basically a consumer/producer setup with the same beans in both deployments on both machines.
ear
ejb-api
com.example.bean
ConsumerBean
ProducerBean
ejb-jar
com.example.bean
ConsumerBeanRemote
ProducerBeanRemote
The ProducerBeanRemote should look up the ConsumerBeanRemote and call its public method.
Our machines are communicating like this:
(Machine A) ProducerBeanRemote --> (Machine B) ConsumerBeanRemote
(Machine A) ProducerBeanRemote --> (Machine C) ConsumerBeanRemote
(Machine A) ProducerBeanRemote --> (Machine D) ConsumerBeanRemote
You get the idea...
So the problem is, it doesn't work. I tried to do a manual lookup using the jboss-as-ejb-client libraries, which didn't work because JBoss locks the EJB selector while starting its container (AND I bet the spec has something to say about manual lookups in a Java EE environment). The next thing I tried was doing a lookup using the feature from Spring, to no avail.
We are using the JBoss Application Server 7.1.1.Final.
I bet there has to be a way to accomplish my setup and I would greatly appreciate any help from the community.
UPDATE:
To connect from ProducerBeanRemote to ConsumerBeanRemote, we need the possibility to specify the remote InitialContext at runtime via configuration.
Properties properties = new Properties();
properties.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
properties.put("remote.connections", "default");
properties.put("remote.connection.default.host", "remote-host");
properties.put("remote.connection.default.port", "4447");
properties.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
EJBClientConfiguration ejbClientConfiguration = new PropertiesBasedEJBClientConfiguration(properties);
ContextSelector<EJBClientContext> ejbClientContextSelector = new ConfigBasedEJBClientContextSelector(ejbClientConfiguration);
EJBClientContext.setSelector(ejbClientContextSelector);
StatelessEJBLocator<T> locator = new StatelessEJBLocator<>(viewType, appName, moduleName, beanName, distinctName);
return EJBClient.createProxy(locator);
The resulting exception is
java.lang.SecurityException: EJB client context selector may not be changed at org.jboss.ejb.client.EJBClientContext.setSelector(EJBClientContext.java:181)
We understand that this exception is thrown because of https://issues.jboss.org/browse/AS7-2998, so the question remains: How can we remotely call the same beans in a clean and configurable way?
Nearly the same question as described here: JNDI lookup from one server to many configurable servers
It seems to be impossible to get a reliant connection between multiple servers via EJB, so we ended up using JMS for our server to server communication. Maybe thats an option for you, too.
You didn't tell me much about your setup or any errors you're getting so I can't really help you troubleshoot them. I just struggled through getting a remote ejb client to work as well and these resources were absolutely invaluable:
https://docs.jboss.org/author/display/AS72/EJB+invocations+from+a+remote+client+using+JNDI
https://docs.jboss.org/author/display/AS71/Remote+EJB+invocations+via+JNDI+-+EJB+client+API+or+remote-naming+project
http://blog.jonasbandi.net/2013/08/jboss-remote-ejb-invocation-unexpected.html#comment-form
http://www.mastertheboss.com/jboss-as-7/jboss-as-7-remote-ejb-client-tutorial/page-2
Good luck!
The setup is possible. There is no need to reengineer the whole code using JMS.
It is possible to call remote ejb beans on different JBOSSAS7 servers from a single POJO client using your own custom Selector for the EJBClientContext. With the code mentioned, you are able to register different scopes for different servers
See an example from the jboss developers here
https://community.jboss.org/thread/223419?tstart=0
https://github.com/wfink/jboss-as-quickstart/blob/ejb-clients/ejb-clients/jboss-api/src/main/java/org/jboss/as/quickstarts/ejb/clients/selector/CustomScopeEJBClientContextSelector.java
I am new to Java EE. At present I am going through The Java EE 6 Tutorial, Volume 1 (Basic Concepts Beta) by Sun Microsystems. To escape from monotonous reading time to time I play with few Java EE projects/codes written by others.
I came from SE. My head is still filled with SE. In SE (two tier application) I use
DATABASE_URL = "jdbc:mysql://something.db_server.com/db_name"
This is how my client knows where the database server is.
In one Java EE example I saw
// Access JNDI Initial Context.
Properties p = new Properties();
p.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
p.put("java.naming.provider.url","jnp://localhost:1099");
p.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
InitialContext ctx = new InitialContext(p);
// Change jndi name according to your server and ejb
HelloRemote remote = (HelloRemote) ctx.lookup("HelloBean/remote");
msg = "Message From EJB --> " + remote.sayHello();
This I understand. The code has url and port number. There is this line
p.put("java.naming.provider.url","jnp://localhost:1099");
Client side knows where is the server by the url and which port to knock. I think the code was written at the time of Java EE 5.
Today I have found another example where Netbeans 7, Java EE 6, and GlassFish 3 are used. The client side code
#EJB
private static MySessionRemote mySession;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
JOptionPane.showMessageDialog(null,
"result = " + mySession.getResult());
}
Here is the link
http://netbeans.org/kb/docs/javaee/entappclient.html
No url and port number are given.
Java EE 6 Development with Netbeans 7 by David R. Heffelfinger has a similar example in chapter 7. The author did not explain how it is done in the book. I think he has done it but I probably missed it…
My question is how the client side locate the server without url? Is it stated in one of those xml files? Client can be in California and the GlassFish Server can be in New York. Can anyone explain it to me or point to any tutorial/blog/article where I can find the answer?
Thank you.
There are two things that are going on here.
The first thing is that the way in which to obtain a reference to a remote EJB is not specified in Java EE. You are at the mercy of how an individual vendor thinks it should be done.
Although JNDI is the de facto standard used for this, even this itself is not mandated.
Example: JBoss up till AS7
In JBoss AS up till AS 7, the following sequence was used to obtain a remote reference:
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
env.put(Context.PROVIDER_URL, "jnp://myserver.example.com:1099");
InitialContext context = new InitialContext(env);
Bean bean = (Bean) context.lookup("myear/MyBean/remote");
Here, the URL of the remote server is provided to the initial context and from that context a bean is retrieved. (Note that you must NOT add the well known "java:/" prefix here, or else it will be intercepted by JNDI and resolved locally, despite doing the lookup on a remote context)
Since this method was as mentioned not standardized, a single vendor can change it completely between releases of implementations. Even for implementations for the same Java EE version.
Example: JBoss AS7
In JBoss AS 7, JBoss wanted to move away from JNDI (because it was not specified that JNDI had to be used) and now it happens in approximately the following way:
You'll first need to put a jboss-ejb-client.properties file on your classpath with the following context:
endpoint.name = client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED = false
remote.connections = default
remote.connection.default.host = myserver.example.com
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS = false
And use code as the following:
Properties env = new Properties();
env.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext context = new InitialContext(env);
Bean bean = (Bean) context.lookup("ejb:/myear/mymodule/MyBean!com.example.Bean");
So from the code it looks like no URL is given, but it's statically hidden in a config file.
Application Client Container
Today I have found another example where Netbeans 7, Java EE 6, and GlassFish 3 are used. The client side code [...]
This is yet another thing. What's demonstrated there is a so-called Application Client Container (aka ACC).
This is different from the example above, where a Java SE application used JNDI to contact the remote server. The Application Client Container is a bit of an obscure thing in Java EE. The idea seems to be that you download the client code dynamically from the Server (like an Applet or Java Web Start app), and that it then magically 'knows' where it originated from. There is very limited support for (static) injection in the main class, which you can use to inject remote beans directly.
The Application Client Container is an idea from the early days of Java EE and as far as I know has never gotten much attention. After all these years it has never advanced much after its initial conception. Since it still requires a ton of vendor specific things to be done, I think most people don't bother with it and just use JNDI.
There would need to be a jndi.properties file with the configuration data in it. A client can't magically know the server to connect to even if it would need to connect to a server running on the same host as the client - there can still be multiple.
The example is very odd though - the EJB annotation would indicate it is supposed to run in a Java EE container, not as a client application.
I would also like to note that invoking EJBs in client applications is not really that common; its more a server side technology. If you want to provide an interface to client applications its far easier and more portable to use for example RESTful webservices through JAX-RS.
I have a message driven bean which connects to a remote HornetQ JMS provider (different for production/testing).
The connection settings are stored either in sun-ejb-jar.xml or as #ActivationConfigProperty annotations directly in the MDB class.
Since all these settings are bundled with the ear file it makes the deployment process quite cumbersome when you want to test in different environments since you have to remember to change the settings all the time.
Do you have any ideas on how I could make my application read this settings from the server?
I thought of creating some custom resources and read them with #Resource, but I don't know how to make the MDB read those settings because the #Resource injection AFAIK takes place after the MDB is already initilized...
EDIT
To clarify: What I'm looking for is something like sun-ejb-jar.xml configuration file which I should install on each server with specific configuration (for ex, different JMS providers - topics/queues, etc). But my ear app should be unchanged. It should automatically load the enviroment from each server. Does it make sense?
Do you have any ideas on how I could make my application read this settings from the server?
JMX-Mbeans can be used to connect with the server. Below is the sample code to connect with the server & fetch information from it, may help you to get idea of it.
//---
Hashtable props = new Hashtable();
props.put(InitialContext.PROVIDER_URL, "jnp://localhost:1099");
props.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
InitialContext ctx = new InitialContext(props);
MBeanServerConnection serverConn = (MBeanServerConnection)ctx.lookup("jmx/rmi/RMIAdaptor");
Set<Object> listOfBeans = serverConn.queryMBeans(null, null); // find-all
for(Object o : listOfBeans){
ObjectInstance beanInfo = (ObjectInstance) o;
System.out.println(beanInfo.getObjectName());
}
//---
It outputs the registered topics/queues like jboss.mq.destination:service=Topic,name=ProvisioningResponseTopic along with other stuff.
MBeans can also be used to get other information like ports, binding-address, domain etc.
Note : The above code is JBoss specific, but same can be achieved for Glassfish.
I don't know much about Glassfish. I think there is Application Server Management eXtension (AMX) to accomplish it.
When I try the following lookup in my code:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
return (DataSource) envCtx.lookup("jdbc/mydb");
I get the following exception:
java.sql.SQLException: QueryResults: Unable to initialize naming context:
Name java:comp is not bound in this Context at
com.onsitemanager.database.ThreadLocalConnection.getConnection
(ThreadLocalConnection.java:130) at
...
I installed embedded JBoss following the JBoss wiki instructions. And I configured Tomcat using the "Scanning every WAR by default" deployment as specified in the configuration wiki page.
Quoting the config page:
JNDI
Embedded JBoss components like connection pooling, EJB, JPA, and transactions make
extensive use of JNDI to publish services. Embedded JBoss overrides Tomcat's JNDI
implementation by layering itself on top of Tomcat's JNDI instantiation. There are a few > reasons for this:
To avoid having to declare each and every one of these services within server.xml
To allow seemeless integration of the java:comp namespace between web apps and
EJBs.
Tomcat's JNDI implementation has a few critical bugs in it that hamper some JBoss
components ability to work
We want to provide the option for you of remoting EJBs and other services that can > be remotely looked up
Anyone have any thoughts on how I can configure the JBoss naming service which according to the above quote is overriding Tomcat's JNDI implementation so that I can do a lookup on java:comp/env?
FYI - My environment Tomcat 5.5.9, Seam 2.0.2sp, Embedded JBoss (Beta 3),
Note: I do have a -ds.xml file for my database connection properly setup and accessible on the class path per the instructions.
Also note: I have posted this question in embedded Jboss forum and seam user forum.
Thanks for the response toolkit.... yes, I can access my datasource by going directly to java:jdbc/mydb, but I'm using an existing code base that connects via the ENC. Here's some interesting info that I've found out ....
The above code works with JBoss 4.2.2.GA and here's the JNDI ctx parameters being used:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces:
org.jboss.naming:org.jnp.interfaces
The above code works with Tomcat 5.5.x and here's the JNDI ctx parameters being used:
java.naming.factory.initial=org.apache.naming.java.javaURLContextFactory
java.naming.factory.url.pkgs=org.apache.naming
The above code fails with Embedded JBoss (Beta 3) in Tomcat 5.5.x with the above error message.
java.naming.factory.initial=org.apache.naming.java.javaURLContextFactory
java.naming.factory.url.pkgs=org.apache.namingThe above code fails with the above error using JBoss Embedded in tomcat 5.5.x
Anyone have any thoughts I what I need to do with configuring embedded JBoss JNDI configuration?
java:comp/env is known as the Enterprise Naming Context (ENC) and is not globally visible. See here for more information. You will need to locate the global JNDI name which your datasource is regsitered at.
The easiest way to do this is to navigate to JBoss' web-based JMX console and look for a 'JNDIView' (not exactly sure of the name - currently at home) mbean. This mbean should have a list method which you can invoke, which will display the context path for all of the JNDI-bound objects.
I had some similar issue with Jboss Embedded and i finally fix playing in the file:
test-Datasource-ds.xml
adding
<mbean code="org.jboss.naming.NamingAlias" name="jboss.jmx:alias=testDatasource">
<attribute name="FromName">jdbc/Example DataSource</attribute>
<attribute name="ToName">java:/testDatasource</attribute>
</mbean>
The problem was jboss add the prefix java:/ for all data source declared. So finally i had a datasource named testDatasource, overrided with that directive to jdbc/Example DataSource
Hope it works