BEA Logo BEA WebLogic Enterprise Release 5.0

  Corporate Info  |  News  |  Solutions  |  Products  |  Partners  |  Services  |  Events  |  Download  |  How To Buy

 

   WLE Doc Home   |   J2EE Programming & Related Topics   |   Previous   |   Next   |   Contents   |   Index

Using RMI with Client-Side Callbacks

This topic includes the following sections:

Understanding Server-to-Server Communication

Server-to-server communication allows WebLogic Enterprise (WLE) applications to invoke remote objects and handle invocations from those remote objects (referred to as callback objects). The remote objects can be either inside or outside of a WLE domain.

WebLogic Enterprise (WLE) RMI supports client-to-server, client-to-client, and server-to-client invocations, with callbacks from server-side objects to clients. Clients can be applets or full Java client applications.

Joint Client/Server Applications

In simple usage, client applications invoke methods on a remote object. The server applications implement the methods of the remote object. The remote objects in the server application live within the WLE domain that supports security and transactions. These remote objects in the server application are referred to as WLE objects.

Server applications can act as client applications of other server applications. Server-to-server communication allows client applications to act as server applications for requests from other client applications or from WLE server applications.

The server-to-server communication functionality is available through a callback object. A callback object has two purposes:

Callback objects are not subject to WLE administration, they should be used when transactional behavior, security, reliability, and scalability are not important.

Callback objects are implemented in joint client/server applications. A joint client/server application consists of the following:

Figure 4-1 shows the structure of a joint client/server application.

Figure 4-1 Structure of a Joint Client/Server Application

Joint client/server applications use RMI on IIOP to communicate with the WLE server in an asymmetric fashion. As indicated in the figure, the following operations are executed:

  1. A server gets an object reference from some source. It could be a naming service or it could be passed in through a client, but not located in that client. Since the object reference is not located in a client connected to an ISH, the outgoing call cannot be made using the bidirectional method. The WLE server invokes on the object reference.

  2. On the first invoke, the routing code invokes a service in the ISL and passes in the host/port.

  3. The ISL selects an ISH to handle the outbound invoke and returns the ISH information to the WLE server.

  4. The WLE server invokes on the ISH.

  5. The ISH determines which outgoing connection to use to send the request to the client. If none is connected, the ISH creates a connection to the host/port.

  6. The client executes the method and sends the reply back to the ISH.

  7. The ISH receives the reply and sends it to the WLE server.

Use of callback objects in Java applets is limited due to Java applet security mechanisms. Any Java applet run-time environment that allows a Java applet to create and listen on sockets (via the proprietary environment or protocol of the Java applet) can act as a joint client/server application. However, if the Java applet run-time environment restricts socket communication, the Java applet cannot act as a joint client/server application.

Joint client/server applications use RMI on IIOP to communicate with the WLE server applications which work in an asymmetric fashion, as shown in Listing 4-1. Joint client/server applications can invoke methods on any callback object, and are not restricted to invoking callback objects implemented in joint client/server applications connected to an ISH. Asymmetric IIOP forces the WLE infrastructure to search for an available ISH to handle the invocation. The ISL controlling the ISH must have been configured with the -O option to support callbacks

For information on the IIOP Server Listener (ISL), see the Administration Guide in WebLogic Enterprise online documentation.

For a more detailed description of asymmetric IIOP, see the CORBA Java Programming Reference in the WebLogic Enterprise online documentation.

For more information about management and configuration on remote client applications, see Managing Remote Client Applications (WLE Systems) in the WebLogic Enterprise online documentation.

Note: A remote joint client/server is a client that implements server objects to be used as callback objects. The server role of the remote joint client/server is considerably less robust than that of a WLE server. Neither the client nor the server has any of the WLE administrative and infrastructure components, such as tmadmin , JNDI registration, and ISL/ISH (hence, none of scalability and reliability attributes of WLE).

When do I need to use callbacks?

In WLE, a particularly useful feature of RMI is that you can use it to do client callbacks from Enterprise Java Bean (EJB) servers. Clients cannot advertise EJB implementations, but they can advertise RMI implementations. So if a client wants to be called back from an EJB instance, it should create an RMI object and pass the reference to the EJB instance. The EJB instance can then invoke the client back by using the RMI reference.

In practical use, being able to use a remote object as a parameter or a return value for a remotely invoked method is convenient for such things as updating the display of an applet in response to server-side events. For example, you could simply export the applet itself as a remote object that registers an interest in server-side events, and whose display changes in response to those events.

Example of Callbacks in RMI

Writing source code for RMI applications that use client-side callbacks differs from standard RMI applications in that you have to include some additional code for a client interface. The remote client must implement the client interface. Also, the remote (server) object will now include objects received from the client and method invocations on those objects.

Figure 4-2 shows the structure of an RMI application that uses client callbacks.

Figure 4-2 RMI with Client-Side Callbacks

Figure 4-3 shows pseudo-code to illustrate the client-server interaction in a callback scenario.

Figure 4-3 Anatomy of RMI Client-Side Callbacks

The following sections provide a code example of a simple application that illustrates RMI client callbacks.

The RMI Client Interface

Listing 4-1 shows the client interface. The client interface declares two business methods: IsGoodObject and IsRightValue .

Listing 4-1 CallbackClientIntf.java - A Client Interface


/* Copyright (c) 1999 BEA Systems, Inc. All Rights Reserved */

import java.rmi.*;

/**
* CallbackClientIntf interface contains following methods
* IsGoodObject(Obj, Obj): compare 2 objects,
* IsRightValue(long, long): compare 2 longs,
*/
public interface CallbackClientIntf extends Remote
{
public static final String NAME = "CallbackClientIntf";

public boolean IsGoodObject(Object Obj1, Object Obj2) throws RemoteException;
public boolean IsRightValue(long val1, long val2)
throws RemoteException, Exception;

} // end CallbackClientIntf


The RMI Client

Listing 4-2 shows the RMI client implementing the client interface.

As shown in the bold code, the client does the following:

  1. Implements the methods defined in the client interface, CallbackClientInf.java . (See all the bold code that appears between the star comment lines //******)

    public boolean IsGoodObject(Object Obj1, Object Obj2) . .
    .
    .
    public boolean IsRightValue(long val1, long val2) . .
    .
    .

  2. Looks up the server object:

    Object o = getContext(url).lookup(Callback.NAME);
    server = (Callback)PortableRemoteObject.narrow(o, Callback.class);

  3. Sends the client object to the server:

    int s = server.register(new CallbackClient());

  4. Invokes the business methods on the server object:

    String errMsg = server.sendObject(new DataObject("dataobj"));

    The sendObject method does a callback on the client object.

  5. Invokes another business method on the server object:

    String errMsg = server.sendLong(12345);

    The sendLong method does a callback on the client object.

    Listing 4-2 CallbackClient.java - A Client That Implements the Client Interface


    /* Copyright (c) 1999 BEA Systems, Inc. All Rights Reserved */

    import java.util.Hashtable;
    import java.rmi.RemoteException;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.rmi.PortableRemoteObject;

    /**
    * CallbackClient will do following steps:
    * 1. server.register(ClientObj): send a client object to server
    * 2. server.sendObject(DataObj): send a dataobject to server.
    * server invokes ClientObj.IsGoodObject(obj, obj)
    * 3. server.sendLong(value): send a long number to server.
    * server invokes ClientObj.IsRightValue(val, val)
    */
    public class CallbackClient implements CallbackClientIntf
    {
    static Callback server; // An instance of the CallbackClientIntf

    //*************************************************************
    // Implement methods of CallbackClientIntf
    // Compare 2 objects, this method is for clientobject
    public boolean IsGoodObject(Object Obj1, Object Obj2) throws RemoteException
    {
    return (Obj1.equals(Obj2));
    }

    // Compare 2 longs, this method is for clientobject
    public boolean IsRightValue(long val1, long val2) throws RemoteException
    {
    return (val1 == val2);
    }
    //*************************************************************

    private static void usage()
    {
    System.out.println("Usage: java CallbackClient 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[])
    {
    if (argv.length < 1) usage();
    String url = argv[0];
    try
    {
    Object o = getContext(url).lookup(Callback.NAME);
    server = (Callback)PortableRemoteObject.narrow(o, Callback.class);
    }catch(Exception e)
    {
    System.out.println("exception in lookup server obj" + e);
    }

    //1: register ClientObject to server
    try
    {
    int s = server.register(new CallbackClient());
    if (s == Callback.FAILURE)
    {
    System.out.println("1. Couldn't send client object to server");
    System.exit(1);
    }
    else
    System.out.println("1. Success sending ClientObject to server");
    }
    catch(Exception e)
    {
    System.out.println("exception in rmiRegister: "+e);
    System.exit(1);
    }

    //2: invoke business method from server
    // send a dataobject to server
    try
    {
    String errMsg = server.sendObject(new DataObject("dataobj"));
    if (!errMsg.equals("")) {
    System.out.println("2. "+errMsg);
    } else {
    System.out.println("2. success on send data object to server");
    System.out.println(" and server callback client using ClientObject");
    }
    }
    catch(Exception e)
    {
    System.out.println("exception in sendObject(obj): "+e);
    }

    //3: invoke business method from server
    // send a string to server
    try
    {
    String errMsg = server.sendLong(12345);
    if (!errMsg.equals("")) {
    System.out.println("3. "+errMsg);
    } else {
    System.out.println("3. success on send long value to server");
    System.out.println(" and server callback client using ClientObject");
    }
    }
    catch(Exception e)
    {
    System.out.println("Exception in sendLong(value): "+e);


The RMI Remote Interface

Listing 4-3 shows the RMI remote interface, in which we declare the business methods:

public int register(Object callbackObj) throws RemoteException;
public String sendObject(Object Obj)throws RemoteException;
public String sendLong(long val) throws RemoteException, Exception;

Listing 4-3 Callback.java - A RMI Remote Server Interface


/* Copyright (c) 1999 BEA Systems, Inc. All Rights Reserved */

import java.rmi.*;

/**
* Callback interface contains following methods
* register(callBack): send clientcallback obj to server
* sendObject(Obj): send an object to server
* sendLong(Val): send a long value to server
*/
public interface Callback extends Remote
{
public static final String NAME = "Callback";
public static final int FAILURE = -1;
public static final int SUCCESS = 0;

public int register(Object callbackObj) throws RemoteException;
public String sendObject(Object Obj)throws RemoteException;
public String sendLong(long val) throws RemoteException, Exception;

} // end Callback


The Remote Object (RMI Server)

Listing 4-4 shows the remote object implementation of the business methods in the RMI remote interface.

Listing 4-4 CallbackImpl.java - A Remote Object that Implements the RMI Remote Interface


/* Copyright (c) 1999 BEA Systems, Inc. All Rights Reserved */

import java.util.Hashtable;
import java.rmi.*;
import java.rmi.server.*;
import javax.naming.*;

/**
* Implements the methods defined in the Callback remote interface.
*/
public class CallbackImpl implements Callback
{
private Object callbackObj; // Object on client to verify parameters.

// remember clientobject sent to server
public int register(Object callbackObj) // throws RemoteException
{
if (callbackObj == null) return Callback.FAILURE;
this.callbackObj = callbackObj;
return Callback.SUCCESS;
}

// send regular dataobject to server
// This method returns empty string on success or else error message.
public String sendObject(Object Obj) throws RemoteException
{
// client call_back
Object tmpObj = new DataObject("dataobj");
if (!(callbackObj instanceof CallbackClientIntf))
return "ClientObject is not instance of CallbackClientIntf at server side";

// client call_back object
if (((CallbackClientIntf)callbackObj).IsGoodObject(Obj, tmpObj))
return "";
else
return "fail on send dataobject to server";
}

// send native type long to server
// This method returns empty string on success or else error message.
public String sendLong(long val) throws RemoteException, Exception
{
// client call_back
if (!(callbackObj instanceof CallbackClientIntf))
return "ClientObject is not instance of CallbackClientIntf at server side";

// client call_back object
if (((CallbackClientIntf)callbackObj).IsRightValue(val, 12345))
return "";
else
return "fail on send long value to server";
}

/**
* The main() method creates an instance of CallbackImpl class
* and invokes the rebind() method of JNDI to register the
* new objects. It registers the objects with the name Callback
* and also inform you that it has successfully completed
* the registration process.
*/
public static void main(String args[])
{
try{
Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL,"");
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.beasys.jndi.WLEInitialContextFactory");
Context ctx = new InitialContext(env);
ctx.rebind(Callback.NAME, new CallbackImpl());

System.out.println("CallbackImpl created and bound in JNDI to name "
+ Callback.NAME);
}
catch (Exception e)
{
System.out.println("caught exception:"+e);
}
}//end main()
} // end CallbackImpl


Running the RMI Callback Example

If you would like to run the callback example, do the following:

  1. Make sure that you development environment is properly configured for compiling and running the example, as explained in the topic Setting Up Your WLE Development Environment.

  2. Create a directory where you want to build and run the example. (For example D:\work\rmi_callback )

  3. Cut and paste the code examples provided in the previous sections into four appropriately named Java source files:

  4. Refer to the topic Extra Files Needed to Run the Callback Example. Cut and paste the code for these files into appropriately named files:

  5. Modify the file Callback.ubb (shown in Listing 4-5) so that it indicates the correct values for TUXDIR, APPDIR, and so on. To determine all the changes you need to make, look for the #CHANGEME comments in the file and simply edit those lines as needed. The code you need to modify on each line is shown in bold before a #CHANGEME comment.

  6. Compile the Java source files:

    javac *.java

  7. Run the WebLogic RMI compiler on CallbackImpl.class and CallbackClient.class files as follows:

    java weblogic.rmic CallbackImpl CallbackClient

  8. Run the buildjavaserver command on the XML file:

    buildjavaserver testserver.xml

  9. Set the WLE environment variables APPDIR and TUXCONFIG to indicate the location of your example application and tuxconfig file, respectively.

    Environment Variable

    Example Setting

    APPDIR

    For example, on Windows NT:

    set APPDIR=D:\work\rmi_callback

    TUXCONFIG

    For example, on Windows NT:

    set TUXCONFIG=D:\work\rmi_callback\tuxconfig

  10. Generate a tuxconfig file based on Callback.ubb as follows:

    tmloadcf -y Callback.ubb

  11. Start the WLE server:

    tmboot -y

  12. Run the client:

    java CallbackClient corbaloc://<YourMachineID> :10000

    For example:

    java CallbackClient corbaloc://SAMS :10000

    If the example runs successfully, the following messages are displayed on the screen:

    1. Success sending ClientObject to server
    2. success on send data object to server
    and server callback client using ClientObject
    3. success on send long value to server
    and server callback client using ClientObject

  13. Stop the WLE server:

    tmshutdown -y

  14. To remove the generated files, you can use the cleanup.cmd provided in Listing 4-10, or a similar script.
Extra Files Needed to Run the Callback Example

The following files are provided for your convenience. You can cut and paste the code for each file into an appropriately named ASCII file, and use the files to build and run the callback example in your WLE environment. The files provided here are:

Notice that this example uses a startup properties file to register RMI implementations at startup. (The Hello World example shown in Getting Started with RMI - a Hello World Example, registers the RMI implementations by means of ServerImpl.java in a different way.) For more information on using a startup properties file, see Using a Startup Properties File,

Listing 4-5 Callback.ubb


# Copyright (c) 2000 BEA Systems, Inc. All Rights Reserved


*RESOURCES
IPCKEY 80952
MASTER SITE1
MODEL SHM
LDBAL Y


*MACHINES
SAMS LMID=SITE1 #CHANGEME
TUXDIR="d:\wledir" #CHANGEME
APPDIR="d:\work\rmi_callback" #CHANGEME
TUXCONFIG="d:\work\rmi_callback\tuxconfig" #CHANGEME
MAXWSCLIENTS=5

*GROUPS
DEFAULT: LMID=SITE1
STDGRP GRPNO=1 OPENINFO=NONE


*SERVERS
DEFAULT: RESTART=Y MAXGEN=5 REPLYQ=Y CLOPT="-A"
TMSYSEVT SRVGRP=STDGRP SRVID=1 RESTART=Y
TMFFNAME SRVGRP=STDGRP SRVID=2
CLOPT="-A -- -N -M"
TMFFNAME SRVGRP=STDGRP SRVID=3
CLOPT="-A -- -N"
TMFFNAME SRVGRP=STDGRP SRVID=4
CLOPT="-A -- -F"
ISL SRVGRP=STDGRP SRVID=5
CLOPT="-A -- -O -n //SAMS:10000" #CHANGEME

JavaServer SRVGRP=STDGRP SRVID=6
CLOPT="-A -- -M 0 testserver.jar"


*SERVICES

*INTERFACES


Listing 4-6 DataObject.java


/* Copyright (c) 1999 BEA Systems, Inc. All Rights Reserved */

/**
* DataObject is to test WLE RMI client callback
*/
public class DataObject implements java.io.Serializable
{
private String s;

DataObject(String s)
{
this.s = s;
}
public String toString()
{
return s;
}
public boolean equals(Object Obj)
{
return (((DataObject)Obj).s.equals(s));
}
}


Listing 4-7 ServerImpl.java


/* Copyright (c) 1999 BEA Systems, Inc. All Rights Reserved */

import com.beasys.rmi.Startup;
import java.io.*;
import java.util.Properties;

/*
* The ServerImpl class provides code to initialize and stop the server
* invocation. ServerImpl is specified in the buildjavaserver XML input
* file as the name of the server object.
*/
public class ServerImpl extends com.beasys.Tobj.Server {

public boolean initialize(String[] args) {
Properties p = new Properties();
try {
p.load(getClass().getResourceAsStream("startup.properties"));
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
try {
Startup.main(p);
return true;
} catch (Exception e) {
return false;
}
}

public void release() {}
}


Listing 4-8 startup.properties


# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# SYSTEM STARTUP FILES - Client callback
# ------------------------------------------------
#
# Register a startup class by giving it a virtual name and
# supplying its full pathname.
# weblogic.system.startupClass.[virtual_name]=[full_pathname]
#
# Add arguments for the startup class
# weblogic.system.startupArgs.[virtual_name]=[space separated arguments]

weblogic.system.startupClass.Callback=CallbackImpl


Listing 4-9 TestServer.xml


<?xml version = "1.0" ?>

<!-- Copyright (c) 1999 BEA Systems, Inc.
-->
<!DOCTYPE M3-SERVER SYSTEM "m3.dtd">

<M3-SERVER server-descriptor-name = "testserver.ser"
server-implementation = "ServerImpl" >

<ARCHIVE name = "testserver.jar">
<CLASS name="Callback"/>
<CLASS name="CallbackClientIntf"/>
<CLASS name="DataObject"/>
<CLASS name="Callback_WLStub"/>
<CLASS name="Callback_WLSkel"/>
<CLASS name="CallbackClientIntf_WLStub"/>
<CLASS name="CallbackClientIntf_WLSkel"/>
<CLASS name="CallbackImpl"/>
<FILE name="startup.properties" prefix=""/>
</ARCHIVE>

</M3-SERVER>


Listing 4-10 Cleanup.cmd


rm *.class
rm *.jar
rm *.ser
rm tuxconfig
rm stderr
rm stdout
rm tmsysevt.dat
rm -rf .adm