![]() |
The NetRexx Tutorial
![]() |
In this chapter we will analyse some of the most recent goodies available in JDK 1.1, and consequently in NetRexx.
In this chapter we will analyse:
The RMI (Remote Method Invocation) is a technique by which an object on SYSTEM A can call a method in an object on SYSTEM B, located somewhere else in the network.
All the sending of parameters, and retrieving of the result will happen in a transparent way, so that the user (and, before him, the application developper) has the feeling that the method was called locally (like any other method we saw so far).
So far we saw how the methods are pieces of code run locally by an object:
MACHINE A --------- object OBJ method METHOD (...) code for METHOD <--- runs locally (...)
Using RMI we move code for METHOD to be remote.
MACHINE A MACHINE B --------- --------- object OBJ method METHOD method METHOD (...) ========> code for METHOD <--- runs remote <======= (...)
This "extention" of the method across the Network is done using sockets; but all the programming details are hidden to the programmer, who just have to realize that, being the call remote, the chances that "something-goes-wrong" are bigger, so he MUST be more carefull for error handling.
The following picture might help understanding the Client/Server in the RMI implementation.
+-----------------------+ +------------------------+ | CLIENT rmicl.nrx | | SERVER rmise.nrx | | | | | | a = obj.method() +-------+ +-----+ | | |rem obj| | | | | | method|===>| | +---------------+ | | | | S | | | actual OBJ | | | | | O | |===>| method | | | S| | C | |<===| return | | | T| | K | | +---------------+ | | return U| return|<===| |S | | B| | E | |T | | +-------+ T +-----+U | | | S | B | +-----------------------+ +------------------------+
As you see, the REAL object exists on the SERVER; from the SERVER's point of view, the object IS the SERVER.
This is probably the simplest code you can try, in order to implement an application using the Remote Method Invocation.
We'll write a program to grab the time information from another machine (even if, for practical purposes, the example will run Client and Server on the same machine).
+----------------------------------------------------------------------+ | class Time public implements java.rmi.Remote interface |01 | |02 | method sayTime() returns String signals java.rmi.RemoteException |03 +----------------------------------------------------------------------+ Time.nrx | ![]() |
+----------------------------------------------------------------------+ | import java.rmi. |01 | import java.rmi.server.UnicastRemoteObject |02 | |03 | class TimeImpl public extends UnicastRemoteObject implements Time |04 | |05 | properties private |06 | myname |07 | |08 | method TimeImpl(s=String) signals RemoteException |09 | super(); |10 | myname = s; |11 | |12 | method sayTime() returns String |13 | return 'Hello from' myname 'at' xsys.time('N') |14 | |15 | method main(a=String[]) public static |16 | |17 | -- Create and install a security manager |18 | System.setSecurityManager(RMISecurityManager()); |19 | |20 | do |21 | obj = TimeImpl("TimeServer"); |22 | Naming.rebind("//pcl307/TimeServer", obj); |23 | say "TimeServer bound in registry"; |24 | catch e=Exception |25 | say "TimeImpl err: " + e.getMessage(); |26 | end |27 | |28 +----------------------------------------------------------------------+ TimeImpl.nrx | ![]() |
+----------------------------------------------------------------------+ | import java.rmi. -- MUST be here! |01 | |02 | class TimeCl public |03 | |04 | method main(arg=String[]) public static |05 | arg = arg -- keep NR silent |06 | |07 | do |08 | obj = Time Naming.lookup("//pcl307/TimeServer") |09 | message = obj.sayTime(); |10 | catch e=Exception |11 | say "TimeCl exception:" e.getMessage() |12 | end |13 | say message |14 | exit |15 +----------------------------------------------------------------------+ TimeCl.nrx | ![]() |
Provided you have the three above .nrx files stored in the same directory, in order to run the example, you have to issue the following commands, in your shell
............................................................. -- 1. edit the sources and change "pcl307" to your -- node name > edit TimeCl.nrx > edit TimeImpl.nrx -- 2. Compile the 3 programs -- > java COM.ibm.netrexx.process.NetRexxC Time.nrx > java COM.ibm.netrexx.process.NetRexxC TimeCl.nrx > java COM.ibm.netrexx.process.NetRexxC TimeImpl.nrx -- 3. Generate the stubs -- > rmic TimeImpl -- 4. Start the registry -- (WNT;W95) start rmiregistry -- (ditto) javaw rmiregistry -- (UNIX) rmiregistry & > start rmiregistry -- 5. Start the Server part -- > java TimeImpl -- 6. On another window, you can run the -- client > java TimeCl ............................................................. |
What we saw so far might appear a little "too much" for such a simple application. In fact, it is.
In the following example we use what we have learnt to build an application where objects last LONGER than the lifetime of the client application.
+----------------------------------------------------------------------+ | class volt public implements java.rmi.Remote interface |01 | |02 | method get(ch=int) returns int signals java.rmi.RemoteException |03 | method set(ch=int,value=int) signals java.rmi.RemoteException |04 +----------------------------------------------------------------------+ volt.nrx | ![]() |
+----------------------------------------------------------------------+ | -- voltimpl.nrx |01 | -- voltage controller implementation |02 | -- |03 | |04 | import java.rmi. |05 | import java.rmi.server.UnicastRemoteObject |06 | |07 | class voltimpl public extends UnicastRemoteObject implements volt |08 | |09 | properties private |10 | myname |11 | channel = int[100] |12 | |13 | method voltimpl(s=String) signals RemoteException |14 | super(); |15 | myname = s; |16 | |17 | -- set a channel |18 | method set(ch=int,value=int) |19 | say myname 'channel:' ch 'set to:' value |20 | channel[ch] = value |21 | |22 | -- fetch a value |23 | method get(ch=int) returns int |24 | return channel[ch] |25 | |26 | -- main method |27 | method main(a=String[]) public static |28 | |29 | -- Create and install a security manager |30 | System.setSecurityManager(RMISecurityManager()); |31 | |32 | do |33 | obj = voltimpl("voltageserver"); |34 | Naming.rebind("//pcl307/voltageserver", obj); |35 | say "voltageserver bound in registry"; |36 | catch e=Exception |37 | say "voltimpl err: " + e.getMessage(); |38 | end |39 | |40 +----------------------------------------------------------------------+ voltimpl.nrx | ![]() |
+----------------------------------------------------------------------+ | -- voltcl.nrx |01 | -- client example |02 | -- |03 | import java.rmi. -- MUST be here! |04 | |05 | class voltcl public |06 | |07 | method main(args=String[]) public static |08 | arg = rexx(args) |09 | parse arg act ch val -- get args |10 | act = act.upper() -- upperacase the action |11 | |12 | do |13 | -- get the remote object |14 | obj = volt Naming.lookup("//pcl307/voltageserver") |15 | |16 | -- do the job |17 | if act = 'SET' then -- set a channel |18 | do |19 | obj.set(ch,val) |20 | n = obj.get(ch) |21 | end |22 | if act = 'GET' then -- get a channel |23 | do |24 | n = obj.get(ch) |25 | end |26 | catch e=Exception |27 | say "voltcl exception:" e.getMessage() |28 | end |29 | say 'Channel' ch 'value:' n'.' |30 | exit 0 |31 +----------------------------------------------------------------------+ voltcl.nrx | ![]() |
We saw already how to build an RMI application, so I just show again the commands.
............................................................. > edit voltcl.nrx > edit voltimpl.nrx > java COM.ibm.netrexx.process.NetRexxC volt.nrx > java COM.ibm.netrexx.process.NetRexxC voltCl.nrx > java COM.ibm.netrexx.process.NetRexxC voltimpl.nrx > rmic voltimpl > start rmiregistry > java voltimpl > java voltcl set 2 33 > java voltcl get 2 33 <=== This is MAGIC! ............................................................. |
Let's now analyse a real case study. We want to implement some (tough primitive) file access method. Our client application will then be capable to access a Server's file just like if the file was local.
For this project we again need 4 files, which are:
rfile.nrx - the Interface rfileimpl.nrx - the Implementation rfileserv.nrx - the Server's part rfileclie.nrx - the Client's part
+----------------------------------------------------------------------+ | -- rfile.nrx |01 | -- Remote File Access |02 | -- Interface part |03 | -- |04 | |05 | class rfile public implements java.rmi.Remote interface |06 | method setfilename(s=String) signals java.rmi.RemoteException |07 | method exists() returns int signals java.rmi.RemoteException |08 | method list() returns String[] signals java.rmi.RemoteException |09 | method cat() returns String[] signals java.rmi.RemoteException |10 +----------------------------------------------------------------------+ rfile.nrx | ![]() |
+----------------------------------------------------------------------+ | -- rfileimpl.nrx |01 | -- Remote File Access |02 | -- Implementation part |03 | -- |04 | |05 | import java.rmi. |06 | import java.rmi.server.UnicastRemoteObject |07 | |08 | class rfileimpl public extends UnicastRemoteObject implements rfile |09 | |10 | -- |11 | properties private |12 | myname |13 | fid = File |14 | fname |15 | |16 | -- constructor |17 | method rfileimpl(s=String) signals RemoteException |18 | super(); |19 | myname = s; |20 | |21 | -- set the filename |22 | method setfilename(fn=String) |23 | say myname 'selects' fn |24 | fname = fn |25 | fid = File(fn) |26 | |27 | -- check if file exists |28 | method exists() returns int |29 | return fid.exists() |30 | |31 | -- list a directory |32 | method list() returns String[] |33 | return fid.list() |34 | |35 | -- cat a file |36 | method cat() returns String[] |37 | d = xfile(fname) -- use xfile |38 | rc = d.read() -- |39 | say '(cat) File "'fname'" read rc:' rc'.' |40 | |41 | -- I need this till I cannot return REXX |42 | nl = d.lines |43 | s = String[nl] |44 | loop i = 1 to d.line[0] |45 | s[i-1] = d.line[i] |46 | end |47 | return s |48 +----------------------------------------------------------------------+ rfileimpl.nrx | ![]() |
+----------------------------------------------------------------------+ | -- rfileserv.nrx |01 | -- Remote File Access |02 | -- Server code |03 | -- |04 | |05 | import java.rmi. |06 | import java.rmi.server.UnicastRemoteObject |07 | |08 | class rfileserv public |09 | |10 | -- main method |11 | method main(a=String[]) public static |12 | |13 | myname = "remfileaccess" |14 | mynode = "pcl307" |15 | |16 | -- Create and install a security manager |17 | System.setSecurityManager(RMISecurityManager()); |18 | |19 | do |20 | obj = rfileimpl(myname); |21 | Naming.rebind('//'mynode'/'myname, obj); |22 | say 'Bind of' myname 'OK.' |23 | say 'Node is' mynode '.' |24 | say 'SERVER now ready for connections.' |25 | say 'HIT CNTRL-C to ABORT' |26 | catch e=Exception |27 | say 'rfileserv error:' + e.getMessage(); |28 | end |29 | |30 +----------------------------------------------------------------------+ rfileserv.nrx | ![]() |
+----------------------------------------------------------------------+ | -- rfileclie.nrx |01 | -- Remote file Access |02 | -- Client part |03 | -- |04 | import java.rmi. -- MUST be here! |05 | |06 | class rfileclie public |07 | |08 | properties public static |09 | fn |10 | |11 | method help() public static |12 | say 'implemented commands are:' |13 | say 'java rfileclie ls <FILE>' |14 | say ' state <FILE>' |15 | say ' cat <FILE>' |16 | exit 6 |17 | |18 | method ls(fid=rfile) public static |19 | if fid.exists() = 0 then |20 | do |21 | say 'Sorry: remote file "'fn'" does not exist.' |22 | exit 1 |23 | end |24 | dd = String[] |25 | dd = fid.list() |26 | loop i = 0 to dd.length - 1 |27 | say dd[i] |28 | end |29 | |30 | method cat(fid=rfile) public static |31 | if fid.exists() = 0 then |32 | do |33 | say 'Sorry: remote file "'fn'" does not exist.' |34 | exit 1 |35 | end |36 | dd = String[] |37 | dd = fid.cat() |38 | loop i = 0 to dd.length - 1 |39 | say dd[i] |40 | end |41 | |42 | |43 | method main(args=String[]) public static |44 | arg = rexx(args) |45 | parse arg cmd fn |46 | |47 | if cmd = 'help' then |48 | do |49 | help() |50 | end |51 | do |52 | -- get the remote object |53 | fid = rfile Naming.lookup("//pcl307/remfileaccess") |54 | |55 | -- do the job |56 | if fn = '' then fn = '.' |57 | fid.setfilename(fn) |58 | select |59 | when cmd = 'ls' then ls(fid) |60 | when cmd = 'cat' then cat(fid) |61 | otherwise say 'Unimplemented command.' |62 | end |63 | catch e=Exception |64 | say "rfileclie exception:" e.getMessage() |65 | end |66 | exit 0 |67 +----------------------------------------------------------------------+ rfileclie.nrx | ![]() |
RMI is a rather new topic (at least it is in June 1997). You might find some additional information at:
http://chatsubo.javasoft.com/current/doc/tutorial/getstart.doc.html http://www.widget.com/ggainey/java/rmi_talk/rmi_talk.html
If you forget to update the stubs, since you forgot to run "rmic IMPLEMENTATION_FILE", you get a message like:
java.lang.IllegalAccessError: unimplemented interface method at ... (... follows tracedump ...) ...
You should then run rmic IMPLEMENTATION_FILE to have the correct interface.
You might get an error like:
java.lang.NumberFormatException: SERVER error at (TRACE)
You usually clear it stopping and restarting the rmiregistry program.
There are currently problems if the method returns a REXX type. The message you get is something like:
client exception: Error unmarshaling return nested exception is: java.io.NotSerializableException: netrexx.lang.Rexx
*** This section is:*** and will be available in next releases