600 likes | 615 Views
Learn how to handle multiple objects in RMI, securing your application, remote deployment, and handling remote objects in collections effectively with RMI and JNI.
E N D
Lesson 4 Advanced RMI JNI
Posting many objects • What if many people logon to play tic-tac-toe? How do we give each pair their own game? • What if we have many objects in general to post? • There are two ways to deal with this • give each their own name in registry (not good) • bootstrap off a singe factory method (good)
Bootstrapping • For tic-tac-toe example, we could have a method: TicTacToeImpl getGame() throws RemoteException; • As long as both getGame and TicTacToeImpl are setup up as remote, only one remote object need get posted to the registry. • Can create a pool of tic-tac-toe games and assign as they come in. • Will be required for hw2, recommended for hw1. • This is a great simplification vs. binding multiple objects!
Contacting rmi registry • In our simplified application, to get a remote reference we called: Naming.lookup(“tic-tac-toe”); • This assumes the default rmi port (1099) on localhost. • To change ports, run rmiregistery <port> • The more general rmi URL is Naming.lookup(“rmi://host:port/name”); e.g. Naming.lookup(“rmi://yourserver.com:99/tic-tac-toe”);
Listing objects in the registery • Note that the Naming class has a method list which returns a listing of all objects bound to the registry. • It is called from the client as: • String[] bindings = Naming.list(“<rmi url goes here”);
Security issues • Recall that client needs auto-generated up-to-date stub functions. • If these are available locally on the client, there is no security issue. • However, keeping a local installation can be cumbersome. Often stubs are downloaded via other servers (we’ll see how to do this). • In this case, a SecurityManager needs to be installed to ensure that the stubs are not hostile (unless applet is used, which has its own SecurityManager).
RMISecurityManager • Easiest way to do this is to use java.rmi.RMISecurityManager as: System.setSecurityManager(new RMISecurityManager()); • This by default restricts all code from making socket connections. • Obviously this is too strict. Need a policy file to allow client to make network connection to rmi port. It would look something like: grant{ permission java.net.SocketPermission “*:1024-65535”, “connect”} java Client –Djava.security.policy=client.policy
Server-side security/firewalls • For this class we assume you have control over the server security configuration.
RMI application deployment • Very simple if client/server both have up-to-date copies of all class files • However, this is unrealistic and impractical. • Better if client can load dynamically load classes remotely. • RMI provides such a mechanism built on top of standard servers.
Deployment, cont. • For server, following classes must be available to its classloader: • Remote service interface definitions • Remote service implementations • Stubs • All other server classes • For client • Remote service interface definitions • Stubs • Server classes for objects used by the client (e.g. return values) • All other client classes
RMIClassLoader • RMI support remote class loading by specfying the java.rmi.server.codebase property with an apprpriate URL • This is done as: java WhateverImp -Djava.rmi.server.codebase=http://whatever ftp, file, etc. can also be used • Proper remote deployment will be required on the next assignment.
Using Remote Objects in Sets • Recall that equals() and hashcode must be overridden to use Sets. • For remote objects, this would require a network call to the server, which could fail. • Since equals does not throw RemoteException, it cannot be overridden and used remotely. • Thus, must use equals and hashCode methods in RemoteObject class. • These unfortunately only compare stubs, not contents. • Probably not a great idea to do this unless necessary
clone() method • Can not call at all directly for stubs • If you want to clone, simply define a new remote method (e.g. remoteClone), have it call clone locally, and return copy as parameter.
Using synchronized methods in rmi • Recall that remote objects are passed by reference (stub). • Thus, when many users request the same object, they are manipulating a single copy. • If the object is immutable, there is no need to synchronize. • Otherwise, proper synchronization should be done in the regular way – using synchronized methods where appropriate, and synchronized blocks if necessary.
Native Data Types • Native data types • pass by value – parameter copied before remote transmission • exactly like single jvm behavior • machine-independent format used
Objects • Calling sequence for object parameters • pass by value also! • this differs from single-jvm behavior • All objects are completely serialized (deep copy) and sent to remote location. • This can be hugely inefficient. Be very careful when performance is at a premium!
Remote Objects • Java defines a third type of parameter – a remote object. • Remote objects must be setup as rmi distributed objects in the regular way (extend UnicastRemoteObject and implement Remote interface) • Remote objects are pass-by-reference. proxies rather than serialized objects are returned.
Distributed garbage collection • Server keeps track of clients that have requested object. • When client disconnects, they are removed from the list. • Also a timeout mechanism controld by java.rmi.dgc.leaveValue. • Remote object can implement java.rmi.server.Unfererenced to get notification when clients no longer hold live reference. • Bottom line: not much to worry about here.
RMI callbacks • Frequently, a server will need to inform a client asynchronously when an event occurs. e.g. • a player moved in a board game • a certain amount of time passed (ticker) • In such a case, the client has to behave like an rmi server. • There is nothing special about such situations. The client can export a remote object and make it available for the server to use for callback. • See class TimeMonitor example
Creating objects remotely • What we have learned about RMI requires the server to instantiate one or more objects and post them to the registry • What if objects do not exists. Is it possible for the client to remotely create an object on the server? • This can be useful occasionally and is now possible with java Activation framework. • We will not discuss Activation in this class.
JNI • Recall that CORBA allows inter-language remote object calls; RMI does not! • However, JNI allows one to call “native code” directly from java. • Thus, the two together give more CORBA-like capabilities. • rmi calls java remote method, which locally calls C code, which returns java parameter(s), and rmi sends back to client.
What exactly does JNI do? • Provides the glue between java – C/C++ (but no other languages) • Provides the machinery for mapping datatypes. • Can be used to call C/C++ from Java (typical), or Java from C (invocation API) • Does NOT provide network transparency!
Reasons for using JNI • Feature not available in java language. • Code already written in another language, don’t want to rewrite. • Java is slow. • Other language has no additional features per se, but has much better syntax for handling certain operations (Fortran for math).
Problems with JNI • Only provides C/C++ bindings. Going to Fortran, COBOL, Ada, etc. requires extra step. • Not portable • Mapping is not trivial • No built-in security
Basic steps to calling native code • Write java class with a method declared with native keyword. Provide no implementation • public native void sayHello(); • Example above is most simple, but method may pass any parameters or have any return type. • Add a call to System.loadLibrary(“libname”) in the class that declares the native method: • static{ System.loadLibrary(“hello”);}//static means called only once.
Steps, cont. 3. Compile the class • javac Hello.java 4. Produce the C/C++ header files using the javah utility: • javah Hello • This produces the header file Hello.h • Write your implementation file by first copying the function signature produced in the include file. Also, #include the header file. #include “Hello.h”
Steps, cont. 6. Write the implementation in C/C++. This will require using JNI methods to access the data or possibly casts to convert to basic C/C++ types 7. Best technique: Break into two steps. Think of your C/C++ function as a wrapper which accesses the Java data and maps it to C data using JNI methods, then shoves the converted data into a prewritten standalone C program.
Steps, cont. 8. Compile your native method(s) as a shared object (or DLL on Windows). • WARNING: Be sure to point your linker to the include files in /jdk1.3/include and jdk1.3/include/linux (for example). • WARNING: Mixing languages is much easier using a straight C wrapper rather than C++. 9. Set the environment variable LD_LIBRARY_PATH to the shared object directory • Run main Java class.
Mapping of datatypes • C datatypes are platform-dependent. Those in java aren’t. • Thus, JNI defines its own portable C datatypes. • See next slide for mapping of native types.
String mappings • Java and C strings are very different (Unicode vs. 8-bit null terminated). • Thus, JNI cannot simply pass memory from java to C. • Some mapping must occur. • This is handled via C utility functions provided with JNI. • jstring is opaque type, and methods operate directly on/create jstring types.
What does Java pass to my C function? • How are these utility functions provided? • JNIEnv* : A pointer to the the JNI environment. This pointer is a handle to the current thread in the JVM, and contains mapping functions and other housekeeping information. • jobject : A reference to the object that called the native code (this) for non-static methods • or jclass: A descriptor of the class which contains the method for static methods • Any arguments specified by the method.
String functions • The string functions look like this: • jstring NewStringUTF(JNIEnv*, const char[]); • jsize GetStringUTFLength(JNIEnv*, jstring); • void REleaseStringUTFChars(JNIEnv*, jstring, const jbyte[]); • void ReleaseStringChars(JNIEnv*, jstring, const jchar[]); etc. etc.
Object fields • What if we want to go beyond number and string parameters? • It is cumbersome but but not conceptually difficult to call methods that manipulate object state. • The designers of JNI made it difficult in order not to hide the inner layout of java data structures. • Thus, many method calls are required, outlined on the next page.
Test case – Employee class • Imagine the following simple Employee class: public class Employee{ public void raiseSalary(double percent){ this.salary *= 1 + percent / 100; } private double salary; } • We want to write this as a native method
Making raiseSalary native • To make raiseSalary native, we hit the signature with javah and get the following: JNIEXPORT void JNICALL Java_Employee_raiseSalary (JNIEnv*, jobject, jdouble); • Note that second argument is of type jobject since method is non-static. It is like this. • We need to access, change, and set the salary field of the implicit parameter. • This is a several step process.
How to access the fields of implicit parameter • General syntax is: x = (*env)->GetXxxField(env, class, fieldID); • To get the object class, do: jclass class_Employee = (*env)->GetObjectClass(env, obj_this); • To get the fieldID, do: jfieldID id_salary = (*env)->GetFieldID(env,class_Employee, “salary”, “D”); (string D denotes the type – double) • Finally, call SetXxxField to affect change.
Source code for example JNIEXPORT void JNICALL Java_Employee_raiseSalary( JNIEnv* env, jobject obj_this, jdouble byPercent){ jclass class_Employee = (*env)->GetObjectClass(env, obj_this); jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, “salary”, “D”); jdouble salary = (*env)->GetDoubleField(env, obj_this, id_salary); salary *= 1 + byPercent / 100; (*env)->SetDoubleField(env, obj_this, id_salary, salary); }
Accessing static fields • Similar to object fields, but must get class using a different method, e.g.: jclass class_System = (*env)->FindClass(env, “java/lang/System”); • To get the fieldID jfieldID id_out = (*env)->GetStaticFieldID(env, class_System, “out”, “Ljava/io/PrintStream;” • To get static object field jobject obj_out = (*env)->GetStaticObjectField(env, class_System, id_out);
Calling java methods • This is possible and quite straightforward as well. • Simple use (*env)->CallXxxMethod(env, implicit parameter, methodID, explicit parameters). • Xxx can be Void, Int, Object, etc. depending on return type of method. • MethodID is like fieldID – use GetMethodID function to access. • Note that GetMethodID requires class name, method name, AND signature. How to specify signature is detailed in Horstmann ch 11.