今天要评估改造一个实时信息发送平台的工作量,原来的系统里用到了JNP,在调研时,发现了这篇不错的文章。
原文地址:http://www.engfers.com/2008/08/07/exposing_accessing-jboss-jndi-objects_datasources-from-an-external-jvm/。
PROBLEM:
You have some program that needs to access some data sources that are managed by JBoss and accessed via JNDI, but when you start that program, it can’t find that JNDI reference or you get ajavax.naming.NoInitialContextException with the message “Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial”
Example:
Our company uses ATG Portal for our end customer. The ATG Portal + our additions (web aps – WAR’s) to the portal get assembled into an EAR file. The EAR file then is deployed onto JBoss, which contains the Tomcat engine to serve up web apps.
You can easily turn at data source into an ATG Component via their JTDataSource pattern by havning your component have a properties file that says…
$class=atg.nucleus.JNDIReference
JNDIName=java:WhateverDsJndiName
Now that means that ATG will query JBoss’s JNDI container to get connections from the JBoss connection pool manager for your datasource. Well how are these defined? Via JBoss *-ds.xml files!
You just make a MyNewWhatever-ds.xml (“-ds.xml” is important…) that looks like this…
NOTE: Your <jndi-name> must match the name after the colon for JNDIName property for your component. The “java:” part of the JNDIName refers to the scope context for that object.“Java” scoped JNDI components can NOT be seen outside the JVM!!!
Well ATG has a nice little tool to help you load data into your Repository(or just sanity check your repositories) called startSQLRepository. Unfortunately, this script brings up a dumbed down version of the ATG Platform without JBoss, which brings up the repositories, which hits the NewWhateverDsJTDatasource, which queries the JNDI for a connection/transaction manager……
ALARM!!! ERROR!!! EXPLOSIONS!!! It fails with thejavax.naming.NoInitialContextException!!!
JBoss is our connection manager and JNDI container but startSQLRepository doesn’t use JBoss!!!
Oh no we’re screwed!!! Actually we’re not… read on…
SOLUTION:
Well after much reading on JBoss and Redhat’s sites on JBoss JNDI naming service, I found that it’s not that hard to connect to JBoss’s JNDI from an external JVM.
There are 2 main ways you can connect to JBoss’s JNDI Context (see above as well)
The JNDI can now be externalized via these two interfaces. Using JNP, we can create a socket connection to JBoss via the default 1099 port
Luckily JBoss has jar’ed up every-freaking-thing-you-need-in-the-world into a single client jar that your clients must have in their classpath:
# Located in the $JBOSS_HOME/client directory
jbossall-client.jar
You should also include any JDBC drivers for the databases you are connecting to in the client classpath as well.
Next, you pass 3 arguments to the client JVM…
NOTE: If you are doing this over SSL (HTTPS), be ready to deal with Secured Socket handshaking and certificates/etc.
NOTE: Make sure you check your user security information and ports/servlets in the http-invoker.sar. You may have to add extra parameters to pass principal (username & password) information onto the inoker servlet.
And we are done right? We run our client and…
ALARM!!! ERROR!!! EXPLOSIONS!!! It fails with thejavax.naming.NameNotFoundException
What happened?!?! We forgot that data source in JBoss get added to the “java” naming context instead of the global context, and any “java” context object can’t be seen outside of the JVM.
So we need to “tell” the data source (via the -ds.xml file) that it shouldn’t use the java context by default, so just add <use-java-context>false</use-java-context> to your ds file and now your JNDI name will be added to the global context!!
And then they lived happily ever after…
The End