Java RMI with Callable - java

I have a server and several clients. The server should be able to delegate tasks to the clients so I tried to implement RMI. I followed this tutorial and everything is working fine if I use String as param- and/or return-value.
Now the server should send undefined tasks to the clients so I tried to use a Callable as param but the program crashed with a NotSerializableException. Since Callable doesn't implement the Serializeable interface thats the result I expected.
Now I found several sources that use Callable and Runnable as params and that confuses me. Is there any trick to get it to work? Or do i miss something important? Maybe theres a technology that fits better?
Resource1 S. 33
Resource2 s. 5
And heres my code:
// Client
public static void main(String[] args) throws InterruptedException {
App app = new App();
app.startClient();
Thread.sleep(20000);//just for test purpose
}
private void startClient() {
try {
// create on port 1099
Registry registry = LocateRegistry.createRegistry(1099);
// create a new service named myMessage
registry.rebind("calcClient", new CalculateRemoteImpl<String>());
} catch (RemoteException e) {
e.printStackTrace();
}
System.out.println("System is ready");
}
// RemoteInterface
public interface CalculateRemote<T> extends Remote {
public T hello(Callable<T> hello) throws RemoteException;
}
// RemoteInterfaceImpl
public class CalculateRemoteImpl<T> extends UnicastRemoteObject implements CalculateRemote<T> {
public T hello(Callable<T> hello) throws RemoteException {
return (T) ("Hello " + hello);// just print address of object
}
}
.
// Server
public static void main(String[] args) {
App app = new App();
app.doTest();
}
private void doTest() {
try {
// fire to localhost port 1099
Registry myRegistry = LocateRegistry.getRegistry("127.0.0.1", 1099);
// search for myMessage service
CalculateRemote<String> impl = (CalculateRemote<String>) myRegistry.lookup("calcClient");
// call server's method
System.out.println("Message: " + impl.hello(new Callable<String>() {
public String call() throws RemoteException, Exception {
return "hello";
}
}));
System.out.println("Message Sent");
} catch (NotBoundException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
// And the same RemoteInterface
public interface CalculateRemote<T> extends Remote {
public T hello(Callable<T> hello) throws RemoteException;
}
.
// stacktrace
java.rmi.MarshalException: error marshalling arguments; nested exception is:
java.io.NotSerializableException: de.fhb.rmicalcserver.App$1
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:156)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
at $Proxy0.hello(Unknown Source)
at de.fhb.rmicalcserver.App.doTest(App.java:30)
at de.fhb.rmicalcserver.App.main(App.java:18)
Caused by: java.io.NotSerializableException: de.fhb.rmicalcserver.App$1
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
at sun.rmi.server.UnicastRef.marshalValue(UnicastRef.java:292)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:151)

If you want to send objects to clients whose classes aren't deployed at the client you need to take a long look at the RMI codebase feature.

Related

How to remotely connect to multiple Glassfish 4+ instances simultaneously?

I am looking for a way to connect to multiple instances of Glassfish 4+ (JDK7-EE) simultaneously from a stand-alone Swing-based client (JDK7-SE). I successfully connect to a single instance by the following way:
That's the construction of the initial context:
private void connect(String address, String port) {
System.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
System.setProperty("com.sun.corba.ee.transport.ORBTCPTimeouts", "500:30000:20:"+Integer.MAX_VALUE);
System.setProperty("com.sun.corba.ee.transport.ORBTCPConnectTimeouts", "250:90000:100:"+Integer.MAX_VALUE);
System.setProperty("com.sun.corba.ee.transport.ORBWaitForResponseTimeout", "300000");
System.setProperty("java.security.auth.login.config", new File("login.conf").getAbsolutePath());
System.setProperty("org.omg.CORBA.ORBInitialHost", address);
System.setProperty("org.omg.CORBA.ORBInitialPort", port);
InitialContext context = new InitialContext();
}
Look-ups are done by JNDI using a remote interface:
context.lookup("java:global/LawSuiteEE/LawSuiteEE-ejb/GlobalsFacade!ch.lawsuite.control.GlobalsFacadeRemote");
I am using a custom JDBC realm that resides on the server and works fine. On the client side I pass the following login.conf to the initial context (see code above):
default {
com.sun.enterprise.security.auth.login.ClientPasswordLoginModule required debug=true;
};
Authentication is currently done by ProgrammaticLogin:
private void login(String username, char[] password) {
ProgrammaticLogin plogin = new ProgrammaticLogin();
plogin.login(username, password);
}
All of this is working fine! But during startup of the stand-alone client, I want to simultaneously connect to another EJB located on a different server.
Since ProgrammaticLogin has no direct relation to the initial context, I am not sure how to login to two different Glassfish servers simulteneously with different credentials (e.g. username/password) ? Someone any ideas ?
Further examination of the issue has uncovered, that the initial context can only be set once on a per JVM basis. So as soon as the ORB is set up by using System.setProperty(String, String) and the inital context object is instantiated, the design of the SerialInitContextFactory let's you no more change the selected endpoint(s).
Therefore I decide to connect within different JVMs to the different Glassfish servers. So finally I ended up with a separate project that manages the connections to the application server and communicates by RMI with the main project.
Currently my project consists of two different EE projects to which I want connect simultaneously, namely "LawSuiteEE" and "MgmtCenterEE". Here's the new project that handles the connections:
public static void main(String args[]) {
try {
if(args.length==2) {
if(args[1].equals("LawSuiteEE")) {
ILawSuiteEE stub = (ILawSuiteEE) UnicastRemoteObject.exportObject(new LawSuiteEE(), 0);
Registry registry = LocateRegistry.createRegistry(Integer.parseInt(args[0]));
registry.bind("LawSuiteEE", stub);
} else if(args[1].equals("MgmtCenterEE")) {
ILawSuiteEE stub = (ILawSuiteEE) UnicastRemoteObject.exportObject(new MgmtCenterEE(), 0);
Registry registry = LocateRegistry.createRegistry(Integer.parseInt(args[0]));
registry.bind("MgmtCenterEE", stub);
} else {
throw new NumberFormatException();
}
Logger.getLogger(RemoteContext.class.getName()).log(Level.INFO, "Remote context service is listening on port "+args[0]+" for incoming requests delegating to "+args[1]+".");
System.out.println("SIGNAL[READY]");
} else {
throw new NumberFormatException();
}
} catch (RemoteException ex) {
System.exit(1);
} catch (AlreadyBoundException ex) {
System.exit(2);
} catch(NumberFormatException ex) {
System.exit(3);
}
The interface ILawSuiteEE is used for RMI between this and the main project (the second interface IMgmtCenterEE is quite the same):
public interface ILawSuiteEE extends IConcurrentDatastore {
void connect(String address, String port) throws RemoteException;
void disconnect() throws RemoteException;
boolean login(String username, char[] password) throws RemoteException;
}
The appropriate implementation:
public class LawSuiteEE implements ILawSuiteEE {
private InitialContext context;
private ProgrammaticLogin login;
#Override
public void connect(String address, String port) throws RemoteException {
if(context==null) {
try {
System.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
System.setProperty("com.sun.corba.ee.transport.ORBTCPTimeouts", "500:30000:20:"+Integer.MAX_VALUE);
System.setProperty("com.sun.corba.ee.transport.ORBTCPConnectTimeouts", "250:90000:100:"+Integer.MAX_VALUE);
System.setProperty("com.sun.corba.ee.transport.ORBWaitForResponseTimeout", "300000");
System.setProperty("java.security.auth.login.config", new File("login.conf").getAbsolutePath());
System.setProperty("org.omg.CORBA.ORBInitialHost", address);
System.setProperty("org.omg.CORBA.ORBInitialPort", Integer.toString(port));
Logger.getLogger(RemoteDatastore.class.getName()).log(Level.INFO, "Try to connect to application server at "+System.getProperty("org.omg.CORBA.ORBInitialHost")+":"+System.getProperty("org.omg.CORBA.ORBInitialPort")+" ...");
context = new InitialContext();
} catch (NamingException ex) {
throw new RemoteException(ex.getMessage());
}
}
}
#Override
public void disconnect() throws RemoteException {
if(context!=null) {
try {
context.close();
Logger.getLogger(LawSuiteEE.class.getName()).log(Level.INFO, "Server context successfully closed.");
} catch (NamingException ex) {
Logger.getLogger(LawSuiteEE.class.getName()).log(Level.SEVERE, "Couldn't close server context.");
} finally {
this.facades.clear();
this.services.clear();
this.context=null;
}
}
}
#Override
public boolean login(String username, char[] password) throws RemoteException {
login = new ProgrammaticLogin();
return login.login(username, password);
}
}
In the main project I'm going to connect with the following:
public class LawSuiteDatastore extends Thread implements ILawSuiteEE {
private int port;
private int trials;
private boolean ready;
private Process process;
private ILawSuiteEE stub;
public LawSuiteDatastore() {
this.setName("K+: Remote-Datastore-Connection");
this.port = RemoteDatastoreService.cport++;
}
#Override
public void run() {
try {
Tools.log(RemoteDatastoreService.class, Level.INFO, "Starting RMI registry on port "+port+" for connecting to LawSuiteEE server instance.");
this.process = Runtime.getRuntime().exec(new String[] {"java", "-jar", Context.getWorkingDirectory()+"/lib/LawSuiteSX.jar", Integer.toString(port), "LawSuiteEE"});
//<editor-fold defaultstate="collapsed" desc="Redirect Error Stream">
new Thread(new Runnable() {
#Override
public void run() {
try{
try(DataInputStream in = new DataInputStream(process.getErrorStream())) {
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line;
while((line=br.readLine())!=null) {
Tools.log(RemoteDatastoreService.class, Level.SEVERE, line);
}
}
} catch(Exception ex){
Tools.log(MgmtCenterDatastore.class, Level.SEVERE, ex.getMessage());
}
}
}).start();
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Redirect Output Stream">
new Thread(new Runnable() {
#Override
public void run() {
try{
try(DataInputStream in = new DataInputStream(process.getInputStream())) {
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line;
while((line=br.readLine())!=null) {
if(line.contains("SIGNAL[READY]")) { ready=true; }
Tools.log(RemoteDatastoreService.class, Level.INFO, line);
}
}
} catch(Exception ex){
Tools.log(MgmtCenterDatastore.class, Level.SEVERE, ex.getMessage());
}
}
}).start();
//</editor-fold>
// keep thread alive as long process is alive
if(process.waitFor()>0) {
// port was already bound
if(process.exitValue()==2) {
// try it with a different port and start over again
if(trials<3) {
process = null;
port = ++RemoteDatastoreService.cport;
trials++;
if(trials<3) {
start();
}
}
}
}
} catch (IOException ex) {
Tools.log(RemoteDatastoreService.class, Level.SEVERE, ex.getMessage());
} catch (InterruptedException ex) {
Tools.log(RemoteDatastoreService.class, Level.SEVERE, ex.getMessage());
}
}
public boolean isReady() {
return ready;
}
public int getTrials() {
return trials;
}
#Override
public void connect(RemoteDatastore datastore) throws RemoteException {
try {
Tools.log(RemoteDatastoreService.class, Level.INFO, "Locating RMI registry on port "+port+" for connecting to LawSuiteEE server instance.");
Registry registry = LocateRegistry.getRegistry(port);
stub = (ILawSuiteEE)registry.lookup("LawSuiteEE");
stub.connect(datastore);
} catch (NotBoundException ex) {
Logger.getLogger(RemoteDatastoreService.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void disconnect() throws RemoteException {
if(process!=null && stub!=null) {
stub.disconnect();
process.destroy();
} else {
throw new RemoteException("Remote RMI server is not ready.");
}
}
#Override
public boolean login(String username, char[] password) throws RemoteException {
if(process!=null && stub!=null) {
return stub.login(username, password);
} else {
throw new RemoteException("Remote RMI server is not ready.");
}
}
}
How about using multiple threads, one for each server?
You can create a new thread for each connection you need, set up the InitialContext on each thread and connect with the ProgrammaticLogin with different credentials.
You can create your own "custom" thread by implementing the Runnable interface, and create a constructor for it that receives the credentials and/or InitialContext object.
Simple example:
public class MyThread implements Runnable {
private ProgrammaticLogin plogin;
private string user;
private char[] pass;
public MyThread(String username, char[] password,InitialContext context) {
this.user = username;
this.pass = password;
this.plogin = new ProgrammaticLogin();
//add more code here if needed
}
public void run() {
//insert code here when thread will run
}
}
and invoke it thus:
Runnable thread1 = new MyThread("my user1","my pass1",ContextObject1);
Runnable thread2 = new MyThread("my user2","my pass2",ContextObject2);
new Thread(thread1).start();
new Thread(thread2).start();
Of course this is a very simple example and it might not be suitable for your exact needs, but i think it is a good start for what you need. Since each Context and login credentials will run on a different thread they will have their own separate execution stack and you should not experience any concurrency issues (two threads accessing the same object).
However, you should have a good understanding of concurrency and threads otherwise you might run into different exceptions, that are a bit harder to debug due to using multiple threads.
Tom.

Asynchronous web service call in flex and java

How can i access to my Asynchronous web service in java? I made this thing in Flex Builder, and it`s pretty easy: just add web service throw "Data -> Connect to Web Service -> Enter the URL of wsdl" and add this lines:
private function result(e:ResultEvent):void
{
trace(e.result.toString());
}
private function fault(e:FaultEvent):void
{
trace(e.toString());
}
var d:DomainAuth = new DomainAuth();
d.AuthFuncName(login, pass);
d.addEventListener(ResultEvent.RESULT, result);
d.addEventListener(FaultEvent.FAULT, fault);
How can i do this in Java using eclipse EE?
Basically you need to do a SOAP web service client in java if I understand you correctly.
JAX-WS can be your friend. The following code is from here
package simpleclient;
import javax.xml.ws.WebServiceRef;
import helloservice.endpoint.HelloService;
import helloservice.endpoint.Hello;
public class HelloClient {
#WebServiceRef(wsdlLocation="http://localhost:8080/
helloservice/hello?wsdl")
static HelloService service;
public static void main(String[] args) {
try {
HelloClient client = new HelloClient();
client.doTest(args);
} catch(Exception e) {
e.printStackTrace();
}
}
public void doTest(String[] args) {
try {
System.out.println("Retrieving the port from
the following service: " + service);
Hello port = service.getHelloPort();
System.out.println("Invoking the sayHello operation
on the port.");
String name;
if (args.length > 0) {
name = args[0];
} else {
name = "No Name";
}
String response = port.sayHello(name);
System.out.println(response);
} catch(Exception e) {
e.printStackTrace();
}
}
}

Connection refused to host using RMI

First of here is the Exception that I'm getting: http://i.imgur.com/dE5Ou.png
Just to give little background I'm trying to write simple RMI program that connects two remote computers (Client/Server) using java's RMI. I have my Server program up and running but when I run my Client program I get the exception showed above in the link. Since I'm telling it to connect to 192.168.0.104 why is it saying "Connection refused to host: 127.0.1.1"???
Client
public class Client
{
public static void main(String[] args)
{
ServerInterface server;
Registry registry;
try
{
registry = LocateRegistry.getRegistry("192.168.0.104", (new Integer(1099)).intValue());
server = (ServerInterface)Naming.lookup("//192.168.0.104/ServerTest");
String serverString = server.getAndSetMessage("Connecting");
System.out.println("Reply from the server is: " + serverString);
}
catch(Exception e)
{
e.printStackTrace();
System.exit(1);
}
}
}
Server
public class Server extends UnicastRemoteObject implements ServerInterface
{
static String hostName = "192.168.0.104";
String name;
public Server(String name) throws RemoteException
{
super();
this.name = name;
}
public String getAndSetMessage(String message) throws RemoteException
{
return("My name is " + name + " Thanks for message " + message);
}
public static void main(String args[])
{
try
{
String objectname = "ServerTest";
Server theServer = new Server(objectname);
Naming.rebind("//"+hostName+"/"+objectname,theServer);
System.out.println("//"+hostName+"/"+objectname);
System.out.println("I am Registered");
}
catch (Exception ex)
{
System.out.println(ex);
System.exit(1);
}
}
}
You could try to add the following code to the server:
System.setProperty("java.rmi.server.hostname", "192.168.0.104");

How to get DN and password with UnboundID

I need some help concerning UnboundID. I heard it was a great choice but I'm not really used to it.
So I need to make a LDAP listener. On this listener, i should be able to catch bind request (from a ldap browser for example). I wonder how to get the DN and the password. Here is my code for the LDAP listener:
public ResultCode CreateLdapServer () throws LDAPException {
CannedResponseRequestHandler requestHandler = new CannedResponseRequestHandler();
LDAPListenerConfig config =
new LDAPListenerConfig(4243, requestHandler);
try
{
config.setListenAddress(
InetAddress.getByName("localhost"));
}
catch (final Exception e)
{
System.err.println("Unable to create the listen server.");
return ResultCode.PARAM_ERROR;
}
listener = new LDAPListener(config);
try
{
listener.startListening();
System.out.println("Serveur is listening ...");
}
catch (final Exception e)
{
System.err.println("Unable to start listening.");
return ResultCode.LOCAL_ERROR;
}
return ResultCode.SUCCESS;
}
public static void main(String[] args) throws LDAPException {
MyConnection connect = new MyConnection();
connect.CreateLdapServer();
}
I read a lot of UnboundID documentation, but i can't find any simple example of what I need.
Also, i'm not really sure of the utility of CannedResponseRequestHandler. For what i need, is it enough ?
An other question: I'm not sure, but I have the feeling that my server is not listening OR i don't catch anything (when I connect with a ldap Browser, nothing happened). Any Idea / Suggestion ?
Thanks and have a nice day !
EDIT : Thanks to xhochy, I was able to catch the password and the username. As he said, I subclassed LDAPListenerRequestyHandler to override, first, newInstance then ProcessBindRequest. Here is the code (it's absolutely not perfect and it's still a beginning).
public class MyConnection {
private LDAPListener listener;
public MyConnection(){
}
public ResultCode CreateLdapServer() throws LDAPException {
MyLDAPListenerRequestHandler requestHandler = new MyLDAPListenerRequestHandler();
LDAPListenerConfig config =
new LDAPListenerConfig(4243, requestHandler);
try
{
config.setListenAddress(
InetAddress.getByName("localhost"));
}
catch (final Exception e)
{
System.err.println("Unable to create the listen server.");
return ResultCode.PARAM_ERROR;
}
listener = new LDAPListener(config);
try
{
listener.startListening();
System.out.println("Serveur is listening ...");
}
catch (IOException e)
{
System.err.println("Unable to start listening.");
return ResultCode.LOCAL_ERROR;
}
return ResultCode.SUCCESS;
}
public static void main(String[] args) throws LDAPException {
MyConnection connect = new MyConnection();
connect.CreateLdapServer();
}
}
Then the subclass of LDAPListenerRequestHandler:
public class MyLDAPListenerRequestHandler extends LDAPListenerRequestHandler {
#Override
public LDAPListenerRequestHandler newInstance(
LDAPListenerClientConnection arg0) throws LDAPException {
System.out.println("New Instance.");
LDAPConnectionOptions option = new LDAPConnectionOptions();
LDAPConnection connection = new LDAPConnection(option, "yourIPadress", yourport);
System.out.println("Connected to : " + connection.getConnectedAddress()+ " " + connection.getConnectedPort());
return this;
}
#Override
public LDAPMessage processBindRequest(int arg0, BindRequestProtocolOp arg1,
List<Control> arg2) {
System.out.println(arg1.getBindDN());
System.out.println(arg1.getSimplePassword());
return null;
}
}
Thanks again !
Many LDAP server implementations will not return a password and many will not return a password you can use. (ie it maybe a hash).
I would be very curious why there could be a reason to return the password.
-jim
You should subclass LDAPListenerRequestHandler and implement processBindRequest. All the information you are looking for is included in BindRequestProtocolOp (second argument of processBindRequest). Add an empty implementation for all other abstract methods.
If request is your BindRequestProtocolOp instance then you get your information via:
String username = request.getBindDN();
ByteString password = request.getSimplePassword();

How to remotely shutdown a Java RMI Server

I have a very simple Java RMI Server that looks like the following:
import java.rmi.*;
import java.rmi.server.*;
public class CalculatorImpl extends UnicastRemoteObject implements Calculator {
private String mServerName;
public CalculatorImpl(String serverName) throws RemoteException
{
super();
mServerName = serverName;
}
public int calculate(int op1, int op2) throws RemoteException
{
return op1 + op2;
}
public void exit() throws RemoteException
{
try{
Naming.unbind(mServerName);
System.out.println("CalculatorServer exiting.");
}
catch(Exception e){}
System.exit(1);
}
public static void main(String args[]) throws Exception
{
System.out.println("Initializing CalculatorServer.");
String serverObjName = "rmi://localhost/Calculator";
Calculator calc = new CalculatorImpl(serverObjName);
Naming.rebind(serverObjName, calc);
System.out.println("CalculatorServer running.");
}
}
When I call the exit method, System.exit(1) throws the following exception:
CalculatorServer exiting.
java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is:
java.io.EOFException
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:203)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:126)
at CalculatorImpl_Stub.exit(Unknown Source)
at CalculatorClient.<init>(CalculatorClient.java:17)
at CalculatorClient.main(CalculatorClient.java:29)
Caused by: java.io.EOFException
at java.io.DataInputStream.readByte(DataInputStream.java:243)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:189)
... 4 more
[2]+ Exit 1 java CalculatorImpl
What am I doing wrong in this method?
In case anyone is having a similar problem, I figured out the answer myself. Here is my exit() method:
public void exit() throws RemoteException
{
try{
// Unregister ourself
Naming.unbind(mServerName);
// Unexport; this will also remove us from the RMI runtime
UnicastRemoteObject.unexportObject(this, true);
System.out.println("CalculatorServer exiting.");
}
catch(Exception e){}
}
Actually just unregistering and immediately calling System.exit doesn't shut down cleanly. It basically breaks the connection before informing the client that the message was completed. What works is to start a small thread that shuts down the system like:
public void quit() throws RemoteException {
System.out.println("quit");
Registry registry = LocateRegistry.getRegistry();
try {
registry.unbind(_SERVICENAME);
UnicastRemoteObject.unexportObject(this, false);
} catch (NotBoundException e) {
throw new RemoteException("Could not unregister service, quiting anyway", e);
}
new Thread() {
#Override
public void run() {
System.out.print("Shutting down...");
try {
sleep(2000);
} catch (InterruptedException e) {
// I don't care
}
System.out.println("done");
System.exit(0);
}
}.start();
}
The thread is needed to be able to let something happen in the future while still returning from the quit method.

Categories