...
If this is the first time that the client's RMI runtime has seen this particular implementation of the policy, RMI will ask the server for a copy of the implementation. Should the implementation change tomorrow, a new kind of policy object will be returned to the client, and the RMI runtime will then ask for that new implementation.
...
This means that policy is always dynamic. You can change the policy by simply writing a new implementation of the general Policy interface, installing it on the server, and configuring the server to return objects of this new type. From that point on, any new expense reports will be checked against the new policy by every client.
This is a better approach than any static approach because:
If the entry is a valid one-one that matches current policy-the method returns normally. Otherwise it throws an exception that describes the error. The Policy interface is local (not remote), and so will be implemented by an object local to the client-one that runs in the client's virtual machine, not across the network. A client would operate something like this:
Policy curPolicy = server.getPolicy();
start a new expense report
show the GUI to the user
while (user keeps adding entries) {
try {
curPolicy.checkValid(entry); // throws exception if not OK
add the entry to the expense report
} catch (PolicyViolationException e) {
show the error to the user
}
}
server.submitReport(report);
Here's the Policy interface
public interface Policy {
void checkValid(ExpenseEntry entry)
throws PolicyViolationException;
}
Now, don't forget that the Server interface looked like:
import java.rmi.*;
public interface ExpenseServer extends Remote {
Policy getPolicy() throws RemoteException;
void submitReport(ExpenseReport report)
throws RemoteException, InvalidReportException;
}
AND, The server looks like this:
import java.rmi.*;
import java.rmi.server.*;
class ExpenseServerImpl
extends UnicastRemoteObject
implements ExpenseServer
{
ExpenseServerImpl() throws RemoteException {
// ...set up server state...
}
public Policy getPolicy() {
return new TodaysPolicy();
}
public void submitReport(ExpenseReport report) {
// ...write the report into the db...
}
}
This is why I thought (when made the tape...) that the serialization must have somehow captured the methods too. But then, that really doesn't make sense, does it...
What it sounds like is really happening is that they say it is passed, but really an instance is passed and the classLoader then dynamically finds the .class via the RMIClassLoader. then it loads it on the client side, so now they can legally say that it is running on the client side.
The problems are/were that they keep saying
Two things
OK, back.
I looked at the Serialization Specification page and can see nowhere where anything is saved other than object state
So, here is what I figure. The server can pass back either remote objects (which must have an interface on the client side) or non-remote objects (where it sends the copy) ... and possibly might use the remoteloader.
Since the remote objects are instantiated as the interfaces there
is no problem at client compilation time since the interfaces are there
(at the client side too).
What about client compilation and not having the non-remote .classes
present (since they are dynamically loaded at run time.) In this example
they (conveniently) had the non-remote objects interface there as well.
Is this a pre-requisite? No, another nice trick is that one
might have a super of the class locally. So in your code, you
can declare it as the super...so when it actually dynamically loads
a sub, the declaration still works fine.