| ||
Any Java application can access server-side J2EE components simply by setting a few JNDI properties, creating an InitialContext , and looking them up in the global JNDI namespace.
The following example client finds and invokes a remote EJB:
Client invoking an EJB
package com.apress.oc4j.ac.client; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.security.auth.login.LoginException; import com.apress.oc4j.ac.ejb.HelloWorld; import com.apress.oc4j.ac.ejb.HelloWorldHome; public class SimpleClient { private SimpleClient() { } private void run() throws NamingException, RemoteException, CreateException, LoginException { Context ctx = new InitialContext(); HelloWorldHome home = (HelloWorldHome) ctx.lookup("HelloWorldEJB"); HelloWorld bean = home.create(); // call the EJB method System.out.println(bean.getGreeting()); } public static void main(String[] args) throws Exception { new SimpleClient().run(); } }
Notice that the InitialContext constructor in the example is called with no parameters. In this case (as with many clients ), you'll create a jndi.properties file to avoid hard-coding those values. This jndi.properties file looks like this:
java.naming.factory.initial=com.evermind.server.rmi.RMIInitialContextFactory java.naming.provider.url=ormi://myserver.acme.com/oracle
Note | See Chapter 5 for a more detailed discussion of JNDI. |
In OC4J you'll always need to authenticate to access JNDI and other components. Thus, you need to modify the client to authenticate itself to the server before accessing the EJB.
By far, the easiest way to authenticate is by setting two additional properties in the jndi.properties file, like this:
java.naming.security.principal=someUsername java.naming.security.credentials=somePassword
Another option is to dynamically set these JNDI properties based on user input or other processing, instead of using a jndi.properties file, as shown here:
Properties props = new Properties(); props.setProperty("java.naming.factory.initial", "com.evermind.server.rmi.RMIInitialContextFactory"); props.setProperty("java.naming.provider.url ","ormi://myserver.acme.com/oracle"); props.setProperty("java.naming.security.principal", getUsername()); props.setProperty("java.naming.security.credentials", getPassword()); Context ctx = new InitialContext(props);
This second option is usually appropriate for interactive user interfaces.
For greater portability, it's possible for a J2SE application client to authenticate through JAAS by passing in a lot of security- related system properties, creating a LoginContext , passing it a CallbackHandler , and logging in to a JAAS LoginModule . However, it usually makes a lot more sense to just use a J2EE application client, which provides much cleaner and more transparent JAAS integration, as shown in the following code sample.
JAAS Login from a J2SE client
package com.apress.oc4j.ac.j2seclient; import java.io.IOException; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import com.apress.oc4j.ac.ejb.HelloWorld; import com.apress.oc4j.ac.ejb.HelloWorldHome; public class SimpleClient { private static final String USERNAME = "janm"; private static final String PASSWORD = "h881SDF!fo"; private SimpleClient() { } private void run() throws NamingException, RemoteException, CreateException, LoginException { // get login context LoginContext loginContext = new LoginContext(USERNAME, new SimpleCallbackHandler()); try { // get naming context Context ctx = new InitialContext(); // login loginContext.login(); // get the bean HelloWorldHome home = (HelloWorldHome) ctx.lookup("HelloWorldEJB"); HelloWorld bean = home.create(); // call the EJB method System.out.println(bean.getGreeting()); } finally { // logout loginContext.logout(); } } public static void main(String[] args) throws Exception { new SimpleClient().run(); } }
Once you have all the code, you'll need to create a build script that will compile all source codes and package the client. You'll create a simple Ant build script for this, as shown here:
<project name="sample" basedir="." default="dist"> <!-- configure the basic directory properties --> <property name="dir.java.src" value="src/java"/> <property name="dir.java.build" value="build"/> <property name="dir.conf.src" value="src/conf"/> <property name="dir.lib" value="lib"/> <property name="dir.dist" value="dist"/> <path id="project.classpath"> <fileset dir="${dir.lib}"> <include name="*.jar"/> </fileset> </path> <!-- Create needed directories --> <target name="init"> <mkdir dir="${dir.dist}"/> <mkdir dir="${dir.java.build}"/> </target> <target name="compile-client" depends="init"> <javac srcdir="${dir.java.src}" destdir="${dir.java.build}" debug="on" debuglevel="lines,vars,source"> <classpath refid="project.classpath"/> <exclude name="**/Test*.java"/> <exclude name="**/AllTests.java"/> </javac> </target> <target name="dist" depends="compile-client"> <jar destfile="${dir.dist}/ac.jar"> <fileset dir="${dir.java.build}"><include name="**/*.class"/></fileset> <fileset dir="${dir.conf.src}"><include name="*/**"/></fileset> </jar> </target> </project>
The script is quite simple. It merely compiles the sources from the src/java directory and creates a JAR file in the dist directory. It also adds the jndi.properties file to the generated JAR file. You can now use the JAR file to run the client.
Typically, you'll package the client code, the EJB interfaces you're using, and the jndi.properties file in a client JAR. Then, assuming OC4J is running and the EJB has been successfully deployed, you can run the client like this:
java -cp oc4jclient.jar:ac.jar com.apress.oc4j.ac.j2seclient.SimpleClient
Caution | In its manifest file ( META-INF/MANIFEST.MF ), oc4jclient.jar has classpath entries that refer to other J2EE libraries. If you're copying this JAR out of your OC4J installation, make sure to include all of the other JARs your code requires, such as ejb.jar . |
Depending on your environment, it may make more sense to leave the jndi.properties file out of the JAR and include its parent directory on the classpath. This allows you to easily update server and authentication settings without repackaging the client JAR.
| ||