8/11/2006

Access Glassfish DataSource Remotely from Java Applications

My previous post describes how to access a MySql datasource in JBoss from within a standalone java application. This is also supported by Glassfish and Sun Java System Application Server:

1. start MySQL server. If a test database is not present, create it (inside mysql console, run create database test;)

2. put MySQL Connector/J jar (mysql-connector-java-5.0.3-bin.jar) in %SJSAS_HOME%\domains\domain1\lib\ext, and restart Glassfish server.

3. create a jdbc-connection-pool and a jdbc-resource in admin web GUI, which is usually at http://localhost:4848. Setting allow-non-component-callers to true on the jdbc-connection-pool tells Glassfish to open this datasource for remote access, and its JNDI name will be used for remote lookup without java:comp/env prefix. Note that for GlassFish 3.x, allow-non-component-callers property is not needed for standalone client any more.

Alternatively, you can also do it in command line. Edit a file \tmp\mysql-ds.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems Inc.//DTD Application Server 9.0 Domain//EN"
"file:///C:/Sun/AppServer/lib/dtds/sun-resources_1_2.dtd">
<resources>
<jdbc-connection-pool allow-non-component-callers="true"
name="mysql-pool"
datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"
non-transactional-connections="false"
res-type="javax.sql.DataSource" >
<property name="datasourceName" value="mysql"/>
<property name="user" value="root"/>
<property name="password" value="your-password"/>
<property name="port" value="3306"/>
<property name="databaseName" value="test"/>
<property name="serverName" value="localhost"/>
</jdbc-connection-pool>
<jdbc-resource enabled="true" pool-name="mysql-pool"
jndi-name="jdbc/mysql"/>
</resources>
Then run this command to create datasource:
%SJSAS_HOME%\bin\asadmin add-resources \tmp\mysql-ds.xml
4. java client is the same as the one for JBoss, and compile it with the same command.

5. run it with JDK 6 so that I can use wildcard (*) in classpath and don't have to figure out which SJSAS jars to include (see my previous post on this feature):
java -cp C:\ws\nb\scrap\build\classes;%SJSAS_HOME%\lib\*;%SJSAS_HOME%\imq\lib\*;%SJSAS_HOME%\domains\domain1\lib\mysql-java\mysql-connector-java-3.1.13-bin.jar
com.foo.DataSourceTest
Query 'select version()' returned 5.0.18
With JDK 1.5 or earlier, you need to explicitly list all jars in classpath. You need at least javaee.jar, appserv-rt.jar, and a jms-related jar in %SJSAS_HOME%\imq\lib. appserv-rt.jar contains a jndi.properties that configures the client-side naming service provider, and is equivalent to build\classes\jndi.properties in JBoss example. I couldn't find out which jms jar right away, so I use the shortcut classpath format in JDK 6.

If the client and Glassfish server are not on the same host, or Glassfish naming service is not on the default port (3700), then add the two system properties to the above java command:
-Dorg.omg.CORBA.ORBInitialHost=remote-server-host-name
-Dorg.omg.CORBA.ORBInitialPort=server-naming-service-port
GlassFish 3.x provides a gf-client.jar, which is basically empty itself but references (via its META-INF/MANIFEST.MF) ALL GlassFish jar files. In GlassFish 3.1, it's in $GLASSFISH_HOME/lib/gf-client.jar; in GlassFish 3.0.1, it's $GLASSFISH_HOME/modules/gf-client.jar. One can use it in client classpath to include all GlassFish jars. The following is examples with GlassFish 3.1:

javac -cp $GLASSFISH_HOME/lib/gf-client.jar:$GLASSFISH_HOME/domains/domain1/lib/ext/mysql-connector-java-5.1.15-bin.jar:. test/DataSourceTest.java

java -cp $GLASSFISH_HOME/lib/gf-client.jar:$GLASSFISH_HOME/domains/domain1/lib/ext/mysql-connector-java-5.1.15-bin.jar:. test.DataSourceTest

11 comments:

Anonymous said...

I'm having a bit of trouble with this. I'm building the properties at run time, and seem to be getting the following exception:

javax.naming.NoInitialContextException: Cannot instantiate class: org.jnp.interfaces.NamingContextFactory [Root exception is java.lang.ClassNotFoundException: org.jnp.interfaces.NamingContextFactory]
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:657)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:247)
at javax.naming.InitialContext.init(InitialContext.java:223)
at ...
JMSInteractiveSender.main(JMSInteractiveSender.java:45)
Caused by: java.lang.ClassNotFoundException: org.jnp.interfaces.NamingContextFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:242)
at com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:42)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:654)
... 5 more
Exception in thread "main" java.lang.NullPointerException
at JMSInteractiveSender.consumerConnect(JMSInteractiveSender.java:116)
at JMSInteractiveSender.main(JMSInteractiveSender.java:46)

The properties class is initialized as follows:

public JMSInteractiveSender() {
try {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES, "jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
iniCtx = new InitialContext(p);
} catch (NamingException e) {
e.printStackTrace();
}
}


Am I missing something that needs to be changed or added?

admin said...

First, org.jnp.interfaces.NamingContextFactory is only usable with JBoss.

Second, why use all these props in your code? JNDI shouldn't be that hard. All you really need to do is:

(1) use the no-arg new InitialContext() to instantiate context;

(2) include $glassfish_home/lib/appserv-rt.jar and javaee.jar in your classpath, in addition to your application classes. All the JNDI config props are inside appserv-rt.jar but you typically you shouldn't need to worry about it.

By embedding all these props in your code, you app is no longer portable.

For more info, pls see:
https://glassfish.dev.java.net/javaee5/ejb/EJB_FAQ.html

nekojsi said...

Hi howto,

Your post is great, but there is one unclear thing to me in it.
Can the standalone client be e.g. Swing application that runs on a remote location without any GlassFish (or other app server) instance connected to it?
I have tried such a scenario and on the ic.lookup() line I am getting a connection error to address 127.0.0.1 (like ic is trying to connect to localhost), although I have set the necessary properties to point to a remote host.

Thank you.

javahowto said...

A swing application on the client side should also work. The client machine doesn't need to have appserver running, but it needs to have access to certain library jars (Java EE and appserver jars).

abdelatif said...

Greet Post, but your xml misses a ">".
Thanks for sharing.

complexe vitamine b said...

The reasons that so many people download and use MySQL and GlassFish are compelling. In addition to being open source, MySQL and GlassFish are fast, reliable, and easy to use. Though attractive individually, MySQL and GlassFish when used together provide an excellent solution for quickly developing and deploying web applications that are not only secure and reliable, but also scale to meet increasing demand and are highly responsive to user requests.

Anonymous said...

Hi,
great post, thanks.

But I have following problem, when running my remote client, maybe u can help:

SEVERE: Application class remoteclient.RemoteClientApp failed to launch
java.security.AccessControlException: access denied (java.io.FilePermission /Users/jla/NetBeansProjects/RemoteClient/dist/lib/appframework-1.0.3.jar read)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.SecurityManager.checkRead(SecurityManager.java:871)
...


..I have added following code to grant permissions into glassfish/domains/domain1/config/server.policy file:

grant {
permission java.io.FilePermission
"/Users/jla/NetBeansProjects/RemoteClient/dist/lib/appframework-1.0.3.jar", "read";
};


...I added such grant perisssions code also into RemoteClient/client.policy file, but it is probably not the right solution. Please, do u have idea, what should I do?
Thank you in advance,
Jana

javahowto said...

Looks like your permission exception is from the client side. I think you will need to first test without a security manager on the client side. Then you can grant permission to client JVM.

Anonymous said...

I was able to get a connection from the pool using a remote client (standalone client) with allow-non-component-callers=false. It seems it doesn't matter what I set the setting to. Is anybody else seeing this behavior? Btw I use glassfish v3.0.1 (build 22).

javahowto said...

I confirm that in GlassFish 3.x, allow-non-component-callers property on the connection pool does not make any difference. So for GlassFish 3.x, we can ignore it.

Steve Smith said...

Great and Useful Article.

Online Java Training

Java Online Training India

Java Online Course

Java EE course

Java EE training

Best Recommended books for Spring framework

Java Interview Questions








Java Course in Chennai

Java Online Training India