MDB how to get connection settings from server environment - java

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.

Related

How to obtain WebLogic's commonj.work.WorkManager properties from java code?

I am running a web application in WebLogic 11gR1 which makes use of WorkManager. I need to print some properties like: wmName, state, workCount.
This is my java code to get WorkManager info:
InitialContext ic = new InitialContext();
commonj.work.WorkManager wm = (commonj.work.WorkManager) ic.lookup("java:comp/env/MyWorkManager");
The problem I am facing is that from my "wm" variable I cannot access the properties I need, the only properties I can access are these:
But, if I debug it in Eclipse, I can see it contains the properties I need under this structure:
Could you please explain how to get the properties I need (wmName, state, workCount) ?
mayb a better way should be to use JMX and getting those information from WorkManager configuration and runtime mbeans

When to put configuration in file.properties or Jndi

For a long time in many IT services, I see some complex process to manage Java EE application configuration depending of the environments:
- custom tools, with Database or not, to manage replacement in the properties file (unzip war, replace, zip war...)
- Externalize properties file in obscure directory in the server (and some process to update it some time) and some time with a JNDI configuration...
- maven profile and lot of big properties files
But for database connection everybody use jndi datasource.
Why this is not generalized for all configurations that depend of environment ?
Update : I want deal with other variable than datasource, there is no question about datasource : it's in configured in JNDI for Java EE application. After if you want hack JNDI...
Setting up database connectivity (like user name, password, URL, driver etc.) somewhere in the application server has several advantages over doing it yourself in the WAR:
The app server can be a central point where the DB is configured, and you might have several WARs running on that server sharing a DB. So you need to set it up only once.
The DB settings, especially the credentials (username, password) are stored somewhere in the app server instead of somewhere in the WAR. That can have security implications (for instance, restricting access to that file is easier done than in a WAR archive).
You can set up one JNDI path to retrieve a DataSource instance pointing to the DB and do not need to worry about username and password anymore. If you have multiple app servers (one live system, one test system, several developer machines) with different DB URLs and credentials, then you can just configure that in each app server individually and deploy the WAR files without the need to change DB settings (see below).
The server might provide additional services, like connection pools, container managed transactions, etc. So again, you don't have to do it on your own in the WAR.
This is true for other services provided by the app server as well, for example JavaMail.
There are other cases where it you want to configure something that is specific to one web application and does not rely on the environment (the app server), like logging (although that may be set up in the app server, too). In those cases you might prefer using static config files, for instance log4j.properties.
I want to illustrate the third bullet point a bit further ...
Suppose you have one WAR in three app servers (developer machine, test server, live server).
Option 1 (DB setup in WAR)
Create a database.properties :
db.url=jdbc:mysql://localhost:3306/localdb
db.user=myusername
db.pass=mysecretpassword
#db.url=jdbc:mysql://10.1.2.3:3306/testdb
#db.user=myusername
#db.pass=mysecretpassword
#db.url=jdbc:mysql://10.2.3.4:3306/livedb
#db.user=myusername
#db.pass=mysecretpassword
Before you deploy it somewhere, you need to check if your settings are pointing to the right DB!
Also, if you check this file in to some version control system, then you might not want to publish your DB username/password to your local machine.
Option 2 (DB setup in App Server)
Imagine you have configured the three servers with their individual DB settings, and each of them registers the DB with the JNDI path java:database/mydb.
Then you can retrieve the DataSource like so:
Context context = new InitialContext();
DataSource dataSource = (DataSource) context.lookup("java:database/mydb");
This is working on every app server instance and you can deploy your WAR without the need to modify anything.
Conclusion
By moving the configuration to the app server you'll have the advantage of separating settings depending on the environment from your app code. I would prefer this whenever you have settings involving IP addresses, credentials, etc.
Using a static .properties file on the other hand is simpler to manage. I would prefer this option when dealing with settings that have no dependencies to the environment or are app specific.

ActiveMQ 5.10 client in Tomcat 7 with InitialDirContext

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

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

How does the EJB client locate the EJB server without url?

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.

Categories