JINI is a framework that enables us to write Java-based distributed systems. In simple terms, it allows you to expose Java classes as services, which can be then called remotely by other Java based clients over the network. For more details, refer to http://river.apache.org/concepts.html.
Its hosted at http://river.apache.org/.
I came across this in my current project and wanted to explore more about the hows and whys. The problem was acute shortage of documentation on the internet, probably because very few people use this. Also, the documentation and code sample available at the apache-river site were bloated and far from simple. They just created confusion. So I decided to write my own simple implementation using minimal amount of code and configuration.
Our implementation will consist of the following 3 components:
Please note that to keep this simple, we are not using the dynamic code download feature that JINI provides. A downside of that is, the client also has to be bundled with the JINI-specific libraries at runtime. If you ask me thats not too bad actually.
Both the Client and the Server should have the following JINI-libraries dependency:
net.jini
jsk-platform
2.2.2
net.jini
jsk-lib
2.2.2
org.apache.river
reggie
2.2.2
We will have the following two interfaces which we will expose as services:
public interface BankAccountService extends Remote {
long createBankAccount(String userName) throws RemoteException;
}
public interface UserService extends Remote {
List getCurrentUsers() throws RemoteException;
}
Note that these extend the java.rmi.Remote interface and also, each method throws the java.rmi.RemoteException exception. These two interfaces will be shared between the Server and the Client.
public class SimpleNonSecureRmiServer {
private final DiscoveryManagement discoveryManager;
private final LeaseRenewalManager leaseRenewalManager;
public SimpleNonSecureRmiServer() throws Exception {
discoveryManager = new LookupLocatorDiscovery(
new LookupLocator[] { new LookupLocator("jini://localhost:4160") });
leaseRenewalManager = new LeaseRenewalManager();
}
private void exportAndJoinServices() throws Exception {
exportAndJoinService(new UserServiceImpl(), discoveryManager, leaseRenewalManager,
UserService.class.getSimpleName());
exportAndJoinService(new BankAccountServiceImpl(), discoveryManager, leaseRenewalManager,
BankAccountService.class.getSimpleName());
}
private void exportAndJoinService(Remote service, DiscoveryManagement discoveryManager,
LeaseRenewalManager leaseRenewalManager, String serviceName) throws Exception {
// the exporter can export only 1 remote service
// this is VERY important: prevents the class from getting GC-ed
Exporter exporter = getExporter();
Remote exportedService = exporter.export(service);
JoinManager joinManager = new JoinManager(exportedService, new Entry[] { new Name(serviceName) },
(ServiceID) null, discoveryManager, leaseRenewalManager);
}
private Exporter getExporter() {
return new JrmpExporter();
// return new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new
// BasicILFactory());
}
public static void main(String[] args) throws Exception {
SimpleNonSecureRmiServer server = new SimpleNonSecureRmiServer();
server.exportAndJoinServices();
System.out.println("Hello server is ready");
}
}
public class SimpleNonSecureRmiClient {
/**
* Needs to be run with the following JVM args:
*
The reggie should be configured properly, and this must go hand-in-hand with the server. For example, they should use the same protocol, in this case, JRMP. If it uses JERI, then both the server and the reggie should use JERI.
This is the start config file for reggie [start-reggie.conf].
import com.sun.jini.config.ConfigUtil;
import com.sun.jini.start.NonActivatableServiceDescriptor;
import com.sun.jini.start.ServiceDescriptor;
com.sun.jini.start {
private static codebase = "";
private static policy = "";
private static classpath = "lib${/}reggie.jar";
private static config = "config${/}jrmp-reggie.config";
static serviceDescriptors = new ServiceDescriptor[] {
new NonActivatableServiceDescriptor(
codebase, policy, classpath,
"com.sun.jini.reggie.TransientRegistrarImpl",
new String[] { config })
};
}
After that, we need the following file for specifying the protocol as well [jrmp-reggie.conf]:
import net.jini.jrmp.JrmpExporter;
com.sun.jini.reggie {
serverExporter = new JrmpExporter();
}
Refer to the JINI documentation for the configuration details.
To see this in action we need to first start the reggie service. Unpack the start-reggie.zip. From the command prompt, cd into it and:
java -Djava.ext.dirs=lib -jar start.jar config/start-reggie.config
Then, start the server using the main method.
For running the client, you need to create a policy file [called policy.all] containing the following:
grant {
permission java.security.AllPermission;
};
When running the client, give the following JVM arguments:
-Djava.security.policy=/resources/path-to/policy.all
-Djava.rmi.server.RMIClassLoaderSpi=net.jini. loader.pref. PreferredClassProvider
The code can be found in GitHub, under the code/jini/unsecure/plain:
https://github.com/paawak/blog.git