|
|
You can write your own WebLogic Enterprise (WLE) RMI classes and test them in a running WLE application by following the basic guidelines described here. We cover all the steps you need to develop a WLE RMI application from scratch. Various aspects of the Hello World example illustrate the major steps in the development process.
This topic includes the following sections:
Once you have installed the WLE software and the JDK software, you need to make sure that your development environment is properly configured.
Before attempting to compile and build any WLE application, you need to ensure that certain environment variables are set on your system. In most cases, the environment variables TUXDIR and JAVA_HOME are set as part of the WLE installation procedure, and if you are running WLE sample applications the runme
scripts typically set the others for you. However, you need to check all of these environment variables to ensure they reflect correct information and modify them whenever necessary.
Setting Up Your WLE Development Environment
Table 3-1 Setting Environment Variables for WLE Applications
To verify on a Windows system that the information for the environment variables defined during installation is correct, do the following:
Verifying/Setting Environment Variables on Windows NT
The Control Panel appears.
The System Properties window appears.
The Environment page appears.
To change the settings, perform the following steps:
To verify on a UNIX system that the information for the environment variables defined during installation is correct, type the following commands at the prompt:
printenv <ENVIRONMENT_VARIABLE>
To change the settings, type the following commands at the prompt:
export <ENVIRONMENT_VARIABLE>=<DirectoryPath>
This section describes the steps involved in writing the source code for RMI classes, using the Java source files from the WLE RMI Hello World as code examples. We explain what characterizes an RMI application in WLE, and what elements you need to include for it to work.
This section includes step-by-step instructions on how to write RMI classes, compile the source files, generate the needed stubs and skeletons, and deploy the class files in a WLE runtime environment. The steps are:
The Java programming language requires a mapping between the fully-qualified package name of a class and the directory path to that class, so you should decide on package and directory names before you begin writing any Java code.
This mapping allows the compiler to know the directory in which to find the class files mentioned in a program. For the WLE RMI Hello World example, the package name is examples.hello
and the Java source directory is examples/hello
.
A remote object is an instance of a class that implements a remote interface. In WLE, a remote interface must extend the interface java.rmi.Remote
. The rmi.Remote
interface itself contains no method signatures-it simply acts as a tag to identify remote classes.
The interface that you write (extending on rmi.Remote
) should include method signatures that will be implemented in every remote class that implements it.
Your Remote interface should have the following characteristics:
Step 1. Decide on package names and create directories for the source code that reflects the package names.
Step 2. Write the source code for a remote interface.
Note that these requirements are consistent with the Sun JavaSoft RMI model.
Listing 3-1 shows the Remote interface examples.hello.Hello
from our Hello World example. The interface has only one method, sayHello
, which returns a string to the caller.
Listing 3-1
Hello.java - A RemoteInterface
/*
* Copyright (c) 2000 BEA Systems, Inc. All Rights Reserved
*/
package examples.hello;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* This class illustrates an interface for RMI communication.
* @author Copyright (c) 2000 by BEA Systems Inc. All Rights Reserved.
*/
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
A remote object is an instance of a class that implements a remote interface.
Now write the class that can be invoked remotely. The class should implement the remote interface you wrote in Step 2. The remote object is sometimes referred to as an RMI "server."
For example, in the source file examples/hello/HelloImpl.java from the RMI Hello World example we do the following:
Listing 3-2 shows the remote object examples.hello.HelloImpl from our Hello World example.
Listing 3-2 HelloImpl.java - A Remote Object Implementation
/*
* Copyright (c) 2000 BEA Systems, Inc. All Rights Reserved
*/
package examples.hello;
import java.rmi.RemoteException;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
* This class is the sample server for RMI/HelloWorld.
* It illustrates establishing one's self (to JNDI) as a remote object.
* Also, it contains the trivial server method sayHello().
*
* @author Copyright (c) 2000 by BEA Systems Inc. All Rights Reserved.
*/
public class HelloImpl implements Hello {
// Overhead to register one's self:
private static InitialContext initialContext;
private static Context getLocalInitialContext() throws NamingException {
Hashtable env = new Hashtable();
// No Context.PROVIDER_URL indicates native bootstrap
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.beasys.jndi.WLEInitialContextFactory");
initialContext = new InitialContext(env);
return initialContext;
}
public static void release() {
try {
initialContext.unbind("HelloServer");
} catch (Exception e) {
System.out.println("Couldn't unregister the HelloImpl object"
+ e.getMessage());
e.printStackTrace();
}
}
public static void main(String args[]) {
try {
HelloImpl obj = new HelloImpl();
// Bind this object instance to the name "HelloServer"
getLocalInitialContext().bind("HelloServer", obj);
System.out.println("HelloServer bound in JNDI");
} catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
// Method(s) that the Client might call:
public String sayHello() {
return "Hello World!";
}
}
As is required for RMI, our remote object implementation class, examples.hello.HelloImpl , does the following:
public class HelloImpl implements Hello {
public String sayHello() {
return "Hello World!";
}
In our example, we create the instance of the remote class (the actual remote object) in a main method as a part of our implementation class, examples.hello.HelloImpl
.
This is fine-the class that contains the main method and instantiates the remote class can be the implementation class itself. Or, you can have the code that instantiates the remote class in another class entirely.
In the main method we do the following:
Creating an Instance of the Remote Class
HelloImpl obj = new HelloImpl();
getLocalInitialContext().bind("HelloServer", obj);
Note that objects within WLE should be well-behaved to make administration easy. So, for every bind method there should be a corresponding unbind method somewhere. Typically, these methods are called when the server is starting (initialize ) and stopping (release ) as shown in Listing 3-4.
Not doing the unbind() will allow clients to find the object in JNDI but get an error when they cannot use it. When the object is unavailable, it should not be listed in JNDI.
Finally, write a client that invokes methods on the remote object (RMI server). Listing 3-3 shows the client examples.hello.HelloClient
from our Hello World Example.
Listing 3-3
HelloClient.java - A Client That Uses a Remote Service
/* Step 4. Write the source code for a client that invokes methods on the remote object.
* Copyright (c) 2000 BEA Systems, Inc. All Rights Reserved
*/
package examples.hello;
import java.rmi.RemoteException;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
/**
* This class is the sample client for RMI/HelloWorld.
* It illustrates JNDI lookup to find and use a remote object.
*
* @author Copyright (c) 2000 by BEA Systems Inc. All Rights Reserved.
*/
public class HelloClient {
private static void usage() {
System.out.println("Usage: java examples.hello.HelloClient corbaloc://<host>:<port>");
System.exit(1);
}
private static Context getContext(String url) throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, url);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.beasys.jndi.WLEInitialContextFactory");
return new InitialContext(env);
}
public static void main(String[] argv) throws Exception {
if (argv.length < 1) usage();
String url = argv[0];
Object o = getContext(url).lookup("HelloServer");
Hello obj = (Hello) PortableRemoteObject.narrow(o, Hello.class);
System.out.println(obj.sayHello());
}
}
Here is what HelloClient is doing:
Object o = getContext(url).lookup("HelloServer");
Hello obj = (Hello) PortableRemoteObject.narrow(o, Hello.class);
System.out.println(obj.sayHello());
Once an object reference is obtained, the client must narrow it to the appropriate type. Notice the use of PortableRemoteObject.narrow
in the following line from Listing 3-3:
Hello obj = (Hello) PortableRemoteObject.narrow(o, Hello.class); You could using the cast operator here as well. However, we recommend the use of PortableRemoteObject.narrow
to ensure interoperability with EJB container compliant implementations.
A client program that is intended to be interoperable with all compliant EJB Container implementations must use the method javax.rmi.PortableRemoteObject.narrow
to perform type-narrowing of the client-side representations of the home and remote interface.
Programs using the cast operator for narrowing the remote and home interfaces are likely to fail if the Container implementation uses RMI-IIOP as the underlying communication transport.
We suggest that you create a separate "deployment" directory to contain the generated class files. For example, you could create a directory called <MyWLEApps>
/rmi/helloworld/classes
. You must create a deployment directory before you run the javac
compiler on your source files; the javac
command will not create this directory for you. (Note that in Hello World example, the runme
script does create the classes directory for you before it runs the javac
compiler.)
Also before you attempt to compile, set your local CLASSPATH so that it includes the pathname of your deployment directory. For example, if your deployment directory is C:\MyWLEApps\rmi\helloworld\classes
, then make sure this full pathname is in your local CLASSPATH. (Note that in the Hello World example, the runme
script sets this for you.)
Note:
The local CLASSPATH must also include the current directory (.), along all necessary WLE and JDK pathnames. For more on setting up your development environment, refer to the topic Setting Up Your WLE Development Environment.
To compile the source files, change directories (cd
) to the directory that contains the package and run the javac
command on the Java source files. For the RMI Hello World example, you might cd
into <MyWLEApps>
/rmi/helloworld
, then run the following command which would compile the Java source files and put the resulting class files under a directory called classes
:
javac -d classes examples/hello/*.java
The preceding command creates the directory examples/hello
(if it does not already exist) under classes
and places the generated class files in the directory classes/examples/hello
.
(In our Hello World example, this step is accomplished by running the runme
script. See Step 4. Set application environment variables..)
To create a proxy stub file for the client and skeleton file for the server, run the weblogic.rmic
compiler on the fully-qualified package names of compiled class files that contain remote object implementations, like my.package.MyImpl_WLstub
. The weblogic.rmic
command takes one or more class names as an argument and produces class files of the form MyImpl_WLStub.class
and MyImpl_WLSkel.class
.
To generate the stub and skeleton class files for the RMI Hello World example, you would change directories (cd
) into the classes
directory (in our example, <MyWLEApps>
/samples/rmi/helloworld/classes
) and run the weblogic.rmic
command on the generated class file classes/examples/hello/HelloImpl.class
as follows:
java weblogic.rmic -d . examples.hello.HelloImpl The weblogic.rmic
command accepts any option supported by javac
-the options are passed directly to the Java compiler. In the example, the -d
option indicates the root directory in which to place the compiled stub and skeleton class files. So the preceding command creates the following files in the directory <MyWLEApps>
/rmi/helloworld/classes/examples/hello
:
Hello_WLStub.class
Hello_WLSkel.class
The generated stub class implements exactly the same set of remote interfaces as the remote object itself, and handles the necessary encoding (marshalling) and decoding (unmarshaling) of parameters sent across the network.
(In our Hello World example, this step is accomplished by running the runme
script. See Step 4. Set application environment variables..)
A proxy is a class used by the clients of a remote object to handle the marshalling and unmarshaling of parameters across a network. In RMI, the stub and skeleton class files that are generated by the RMI compiler are proxies for the RMI client and RMI server objects, respectively.
In WebLogic RMI, the RMI client stub marshals the invoked method name and its arguments for the client, forwards these to the remote object, and unmarshals the returned results for the client. An RMI client stub is generated by running the WebLogic RMI (weblogic.rmic
) compiler on the fully-qualified package names of compiled class files that contain remote object implementations, like my.package.MyImpl_WLstub
.
The skeleton class is also generated by the WebLogic RMI compiler but the skeleton is not used in WebLogic RMI. Generally, the RMI skeleton would unmarshal the invoked method and arguments on the remote object, invoke the method on the instance of the remote object, and then marshal the results for return to the client. WebLogic Enterprise handles the unmarshaling, method invocation, and marshalling on the RMI server side using reflection. If necessary, you can discard the generated skeleton class files to save disk space because.
The syntax for using the WebLogic RMI compiler is as follows:
java weblogic.rmic
[options] ClassName
The options to the weblogic.rmic
command are shown in the following table.
A Note about Type Narrowing
Step 5. Compile the source code files to create the executable RMI classes.
Step 6. Run the WebLogic RMI compiler on the implementation class to generate stubs and skeletons.
More About Stubs and Skeletons in WebLogic RMI
More About the WebLogic RMI Compiler (weblogic.rmic)
The weblogic.rmic
command also accepts any option supported by javac
-the options are passed directly to the Java compiler.
This section describes how to build an RMI application in WLE. To illustrate this, we explain the commands used in the Hello World runme
script to compile the source files and run the WebLogic RMI code generator.
We explain in more detail how to get things set up and working in the WebLogic Enterprise environment-for Hello World, most of this is also taken care of in our runme
script. (For example, the runme
script generates WLE configuration information and sets up some WLE environment variables)
When you are developing your own RMI classes, you might choose to compile and build manually from the command line, or you might want to use a script similar to the one we provide with the example. Here, we clarify what the manual steps would be and point out where our script accomplishes them.
The steps are:
Building Your RMI Application in the WLE Environment
In Java, you use a Server object to initialize and release the server application. You implement this Server object by creating a new class that derives from the com.beasys.Tobj.Server
class and overrides the initialize
and release
methods. In the server application code, you can also write a public default constructor.
For example:
import com.beasys.Tobj.*; In the Server Description File (server.xml
), which you process with the buildjavaserver
command, you identify the name of the Server object.
This collection of the object's implementation and data constitutes the run-time, active instance of the CORBA object.
When your Java server application starts, the server creates the Server object specified in the XML file. Then, the server invokes the initialize
method. If the method returns true, the server application starts. If the method throws the com.beasys.TobjS.InitializeFailed
exception, or returns false, the server application does not start.
When the server application shuts down, the server invokes the release
method on the Server object.
Any command-line options specified in the MODULE parameter for your specific server application in the SERVERS section of the WebLogic Enterprise domain's UBBCONFIG
file are passed to the public boolean initialize(string[] args)
operation as args
.
For more information about passing arguments to the server application, see the WebLogic Enterprise Administration Guide.
For examples of passing arguments to the server application, see the Guide to the Java Sample Applications in the WebLogic Enterprise online documentation.
Within the initialize
method, you can include code that does the following, if applicable:
Step 1. Create a mechanism for bootstrapping your application.
/**
* Provides code to initialize and stop the server invocation.
* ServerImpl is specified in the server.xml input file
* as the name of the Server object.
*/
public class ServerImpl
extends com.beasys.Tobj.Server {
public boolean initialize(string[] args)
throws com.beasys.TobjS.InitializeFailed {
}
public boolean release()
throws com.beasys.TobjS.ReleaseFailed {
}
}
For most RMI server applications you want client applications to be able to locate easily this object. You need to write the code that registers the RMI objects with JNDI, which is invoked typically as the final step of the server application initialization process.
In our Hello World example we call HelloIMpl.main()
which handles the JNDI registration.
When the WebLogic Enterprise system administrator enters the tmshutdown
command, the server invokes the following operation on the Server object of each running server application in the WebLogic Enterprise domain:
public void release() Within the release()
operation, you may perform any application-specific cleanup tasks that are specific to the server application, such as:
Writing the Code That Creates and Registers an RMI Object or Factory
Releasing the Server Application
Once a server application receives a request to shut down, the server application can no longer receive requests from other remote objects. This has implications on the order in which server applications should be shut down, which is an administrative task. For example, do not shut down one server process if a second server process contains an invocation in its release()
operation to the first server process.
During server shutdown, you may want to include an invocation to unregister each of the server application's RMI objects. For example:
//Unregister the object The invocation of the unbind
method should be one of the first actions in the release()
implementation. The unbind
method unregisters the server application's objects.
Listing 3-4 shows the ServerImpl.java
file for the RMI Hello World example.
Listing 3-4
ServerImpl.java
/*
//Use a try block since the cleanup code shouldn't throw exceptions.
try {
HelloImpl.getLocalInitialContext().unbind("HelloServer");
}
catch (Exception e){
System.out.println("Couldn't unregister the HelloServer object" + e.getMessage());
e.printStackTrace();
}
* Copyright (c) 2000 BEA Systems, Inc. All Rights Reserved
*/
import com.beasys.Tobj.Server;
import examples.hello.HelloImpl;
/**
* This class illustrates an interface for RMI communication.
*
* @author Copyright (c) 2000 by BEA Systems Inc. All Rights Reserved.
*/
public class ServerImpl extends Server {
public boolean initialize(String[] argv) {
try {
HelloImpl.main(null);
} catch (Exception e) {
return false;
}
return true;
}
public void release() {
HelloImpl.release();
}
}
To deploy your WLE RMI application, you need to package it into a Java archive (JAR) file. It is this JAR file that you will call in the WLE configuration file (UBBCONFIG /TUXCONFIG ) during runtime.
This section describes how to create the JAR file using a Server descriptor file. This is what we use in the Hello World example. You could also use the JAR command to assemble your application's classes into a JAR file. But, the <ARCHIVE> element of the server descriptor file provides help by simplifying the process of collecting the files.
You can create the JAR as follows:
The JAR is created in the <ARCHIVE> element. The archive element must be the last element inside the <M3-SERVER> element.
If the XML file contains instructions to create an archive, both the class specified by server_name and the file specified by server_descriptor are stored in the archive. The server_descriptor file is inserted in the archive manifest with the M3-Server tag; this insertion makes the server descriptor the entry point during server execution.
If you do not include the archive element, the buildjavaserver command generates only the server descriptor and writes it in the file specified in the server-descriptor-name attribute of the M3-SERVER element.
Listing 3-5 shows the server descriptor file for our Hello World example.
Listing 3-5 server.xml
<?xml version = "1.0" ?>
<!-- Copyright (c) 2000 BEA Systems, Inc.
All Rights Reserved
-->
<!DOCTYPE M3-SERVER SYSTEM "m3.dtd">
<M3-SERVER server-descriptor-name = "server.ser"
server-implementation = "ServerImpl" >
<ARCHIVE name = "server.jar">
<CLASS name="examples.hello.HelloImpl"/>
<CLASS name="examples.hello.Hello_WLStub"/>
<CLASS name="examples.hello.Hello"/>
</ARCHIVE>
</M3-SERVER>
Note: The deployment directory that contains your RMI classes must be in your local CLASSPATH or buildserver.jar command will fail.
For example:
buildjavaserver <MyServer> .xml
where <MyServer> .xml is your server descriptor file.
This creates the file server.jar
(In our Hello World example, the runme script creates the JAR by running buildjavaserver on the file server.xml . See Step 4. Set application environment variables..)
For more information about using JAR files and Java server startup in WebLogic Enterprise, see Steps for Creating a Java Server Application in the WebLogic Enterprise online documentation.
The configuration file is the primary means of defining the configuration of WLE applications. It consists of parameters that the WLE software interprets to create an executable application.
The UBBCONFIG file is an ASCII version of the configuration file. The TUXCONFIG file is a binary version of the configuration file that you generate from the ASCII version using the tmloadcf command.
In our Hello World example, the UBBCONFIG file is generated by the runme script. You can create this file manually with a text editor, too. Listing 3-6 shows a sample UBBCONFIG file for the Hello World example.
Listing 3-6 UBBCONFIG File for Hello World
*RESOURCES
IPCKEY 55432
DOMAINID Hello
MASTER simple
MODEL SHM
LDBAL N
*MACHINES
DEFAULT:
APPDIR="C:\myWLEapps\rmi\helloworld"
TUXCONFIG="C:\myWLEapps\rmi\helloworld\tuxconfig"
TUXDIR="d:\wledir"
MAXWSCLIENTS=10
"SAMS" LMID=simple
*GROUPS
GROUP1
LMID=simple GRPNO=1 OPENINFO=NONE
GROUP2
LMID=simple GRPNO=2 OPENINFO=NONE
*SERVERS
DEFAULT: CLOPT="-A"
TMSYSEVT SRVGRP=GROUP1 SRVID=1
TMFFNAME SRVGRP=GROUP1 SRVID=2 CLOPT="-A -- -N -M"
TMFFNAME SRVGRP=GROUP1 SRVID=3 CLOPT="-A -- -N"
TMFFNAME SRVGRP=GROUP1 SRVID=4 CLOPT="-A -- -F"
JavaServer SRVGRP=GROUP2 SRVID=6 CLOPT="-A" MODULE="C:\myWLEapps\rmi\helloworld\server.jar"
ISL SRVGRP=GROUP1 SRVID=5 CLOPT="-A -- -n //SAMS:2468"
*SERVICES
After you create the UBBCONFIG file, you must run tmLoadcf on it to create the executable TUXCONFIG file as follows:
tmloadcf -y ubbconfig
In the RMI Hello World example, this is also handled in the runme script.
The TUXCONFIG file contains information used by tmboot to start the servers and initialize the bulletin board of a BEA Tuxedo system bulletin board instantiation in an orderly sequence. The tmadmin command line utility uses the configuration file (or a copy of it) in its monitoring activity. The tmshutdown command references the configuration file for information needed to shut the application down.
You can use the tmconfig command to edit many of the parameters in the executable TUXCONFIG file while your application is running.
Before you can run your application, you must set the following WLE environment variables specific to the application you want to run:
(In our Hello World example, our runme
script sets these variables. See Step 4. Set application environment variables..)
Listing 3-7 shows an example of setting WLE environment variables on a Windows NT system.
Listing 3-7
Setting WLE Application Environment Variables on Windows NT Systems
set APPDIR=C:\myWLEapps\rmi\helloworld
set TUXCONFIG=C:\myWLEapps\rmi\helloworld\tuxconfig
Listing 3-8 shows an example of setting WLE environment variables on a UNIX system.
Listing 3-8 Setting WLE Application Environment Variables on UNIX Systems
export APPDIR=$HOME/myWLEapps/rmi/helloworld
export TUXCONFIG=$HOME/myWLEapps/rmi/helloworld/tuxconfig
Once you have created the RMI classes and built the application, you can test it by running it as a WLE application. To run it:
tmboot -y
java <PackageNameOfClient> <Arguments>
For Our Hello World example, the command to run the client is:
java examples.hello.HelloClient corbaloc://<MyMachineID>
(In out Hello World example, the runme script boots the WLE server and runs the client for you.)
Whenever you are ready to stop the WLE server, type the following at the command line:
tmshutdown -y
(In out Hello World example, the runme script shuts down the WLE server for you.)
In our Hello World RMI example, we use runme scripts that contain DOS or UNIX shell commands to handle a lot of the compile, environment setup, and build tasks detailed in the previous sections. It is very likely you will want to do this as well.
For Hello World, our runme script is used to accomplish the following tasks:
java weblogic.rmic -d <YourClassesDirectory> examples.hello.HelloImpl
runs the weblogic.rmic compiler on the file examples/hello/ HelloImpl.class and puts the resulting stub and skeleton in whatever location you specify as <YourClassesDirectory>
The runme
scripts are located in the Hello World example helloworld
directory. You can use a text editor to view the scripts.
To deploy a WLE application on machines other than your development system, you need to ensure that the appropriate environment variables are set on the target systems.
For systems where you want to deploy a WLE client only, make sure the following environment variables are set.
Deploying Your Application
Deploying the Client
Table 3-2 Environment Variables Needed to Run a WLE Client Application
Note that the main differences between setting environment variables for a client-only deployment versus server development or deployment is that client-only run-time systems require m3envobj.jar
, wleclient.jar
, and wlej2eecl.jar
and do not require the locale/M3
tools. Also, you can run client-only run-time systems with only the JRE bin in the PATH instead of the full JDK bin.
For systems where you want to deploy a WLE server, the environment variables must be set exactly as required for development. See the section Setting Up Your WLE Development Environment
Deploying the Server
|
Copyright © 1999 BEA Systems, Inc. All rights reserved.
|