How does the EJB client locate the EJB server without url? - 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.

Related

Understanding of embeddable EJBContainer

I spent some time in attempts to understand Embeddable Enterprise Bean Applications and still need some clarifications. Assume I need Junit to test EJB application.
So I have assumptions what should happened, please help me figure out correct answer:
Junit is entrypoint and it deploys EJB application to server? So that EJB and Jboss are "embedded" into tests.
Junit and 'EJB' application are two separate JVM processes and they somehow communicate by jndi names or something(I don't use remote EJB).
Real server(JBoss) never used and EJBContainer is just kind of Mock.
Something else.
Edited:
I found an example:
Could you please comment on this code:
#Test
public void test() throws Exception {
String jbossHomeDir = "E:\\dev_station\\java_station\\Serveurs\\jboss-as-7.1.1.Final";
System.setProperty("jboss.home.dir", jbossHomeDir);
StandaloneServer server = EmbeddedServerFactory.create(new File(
jbossHomeDir), System.getProperties(), System.getenv(),
"org.jboss.logmanager");
server.start();
server.deploy(new File("target/classes"));
Context namingContext = server.getContext();
}
The Java EE 6 Tutorial (that first link in the question) says that
The embedded container, the enterprise bean components, and the client all are executed in the same virtual machine using the same classpath.
That is, the JUnit test ("client"), the embedded container (implemented by JBoss Wildfly, Glassfish, etc.), and the Java EE application components (EJBs, etc.) all run in the same JVM instance (the same process).
Nothing is mocked. Container services (transactions, injection, etc.) are provided by an actual Java EE server implementation.
So, the correct alternative between those four listed in the question is number 1.

Lookup of same EJB on multiple servers

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

Configuring and looking up a simple String via JNDI in WebSphere

For the configuration of our applications we want to place some of the configuration in the application server.
We are currently using the WebSphere JNDI facility for looking up urls and datasources.
In addition we want to place simple Strings in the application server. We don't want to use the WebSphere "Enviroment variables".
Can the "Resource Environment" be used for such a purpose? If so, how can it be used?
We'd like to get the Strings with:
InitialContext ctx = new InitalContext();
String myString = (String)ctx.lookup("jndi/string/myString");
Are there any more simple alternatives?
We are using WebSphere Application Server 7.
I believe you can use Name Space Bindings for this (under Environment, Naming, Name Space Bindings.)
Blurb:
Name Space Bindings
Use this page to configure a name binding of a constant string value,
an enterprise bean, a CORBA CosNaming Naming Context or CORBA leaf
node object, or an object that can be looked up by using JNDI.
Take a look at the article Using resource environment providers in WebSphere Application Server
It's written for WebSphere 5 or 6 but the idea should work for version 7 too. Basically you create a custom resource environment provider, which returns objects of your own type via a factory. The factory implements javax.naming.spi.ObjectFactory and gets its input from Websphere.
In my opinion this is a nice solution if you have a few configuration parameters but it can quickly become hard to maintain as the number of parameters grows.

Is there a Java EE way to change user?

Assume the follwing method:
Test getTest()
{
Properties props = new Properties();
props.put(javax.naming.Context.SECURITY_PRINCIPAL, "OtherUser");
props.put(javax.naming.Context.SECURITY_CREDENTIALS, "OtherPassword");
InitialContext ic = new InitialContext(props);
return (TestHome)PortableRemoteObject.narrow(ic.lookup("ejb/Test"),
TestHome.class).create();
}
If this method (in an EJB) is called from a client using user "MyUser" I'd like it to return an EJB with a different caller principal. Calls to Test made by the client would then be noted as being from "OtherUser". i.e. I have programmactially changed a client's caller principal for a given EJB.
However, I find no text on this in the Java EE specs and although it works on our current Java EE app-server (Sybase EAS 4.1), I'd like to ask you if this is a Java EE standard approach or not.
A standard way of handling login/logoff is JAAS. I don't know if it's fair to say that it is the standard, but it's supported by the Java EE servers I've worked with so far (JBoss, Websphere), and apparently also by Sybase EAS.
JBoss, for instance, comes with several predefined login modules for Database and LDAP and whatnot, that you just have to declaratively configure for your application. However, you are also completely free to write your own login module by implementing the respective interface (javax.security.auth.spi.LoginModule).
Take a look at the JAAS Reference Guide.
About your code snippet: I don't know if that'll work on other app servers. This is EJB 1.1, and thus really, really old stuff. You should definitely look at EJB 3.1 and consider modernizing your application anyway.
If I get this right this should link a username to an EJB across more than one methodcall, which in my opinion is a state. I guess the standard for this would be to create a Stateful SessionBean with a setUser(String name) method. In a stateless bean you would have to pass the userName (or properties object) on each methodcall.

MDB how to get connection settings from server environment

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.

Categories