Passing object reference using RMI - java

Hello everyone,
I have been banging my head really hard trying to solve this problem. I really appreciate if anyone can please have a look at my problem and help me.
I have multiple clients that uses Java RMI to establish a connection (register()) and receive a list of connects clients from the server (get()). My goal is to get clients to talk to each other using RMI, without having to register themselves, only the server so far is being register.
I am trying to pass a reference of the client object to the server.
I am getting the following error:
java.rmi.MarshalException: error marshalling arguments; nested exception is:
java.io.NotSerializableException: Client
I do not want to use Serialization (I believe), as I don't want to pass the object itself, but a reference. Client1 should be able to send a message to Client2 by calling client2.messsage(String username, String message);
I believe I should just show you at this point how I have implemented my code:
public interface RMIClientIntf extends Remote {
public void message(String name, String message) throws RemoteException;
public String getName() throws RemoteException;
}
The previous code is what all clients should implement. If the message() function gets called from another client, that means the other class is sending a message.
My client class itself:
public class Client implements RMIClientIntf {
public Client() throws RemoteException { super(); }
public String getName() throws RemoteException { }
}
Now, the Main class that creates an instance of client calls the following, to send the remote object to the server:
final Client client = new Client();
server.register((RMIClientIntf) client, name);
On the server side, the register method is defined as:
public interface RMIServerIntf extends Remote {
public String register(RMIClientIntf cl, String userName) throws RemoteException;
public RMIClientIntf[] get() throws RemoteException;
}
public class Server extends UnicastRemoteObject implements RMIServerIntf {
public String register(RMIClientIntf cl, String userName) throws RemoteException {
ClientStruct newClient = new ClientStruct(cl, userName);
/* Store the ClientStruct */
return new String("SUCCESS");
}
}
After registering, each client will request the list of connected users. The function in the server is the following:
public RMIClientIntf[] get() throws RemoteException {
RMIClientIntf[] users = new RMIClientIntf[openConnections.size()];
/* Fill in users from ClientStruct */
return users;
}
If Client were to also implement Serializable, then the program runs but when client2 calls client1.message(), the message() method gets called for client2 and throws a NullPointer at this point.
I was looking at this link: http://www.exampledepot.com/egs/java.rmi/args_Args.html, which gave me a hint that it should implement Remote but not Serialization to pass by reference. I am not sure why the program is complaining in my case.
I really appreciate any help I can get. I've been trying to fix this problem for a while and I can't seem to find any solution.
Thank you very much!

you need to export Client and send the stub to the server
edit: this should work, it should the same way you exported the server (but without using the rmiregistry)
server.register((RMIClientIntf) UnicastRemoteObject.exportObject(client,0), name);

You can't just pass arbitrary references around via RMI. Client would have to be either Serializable, so it gets passed entire to the target, or an exported remote object itself, so a remote reference to it gets passed and it stays put - i.e. it is a callback.
EDIT: as the latter appears to be your intention, all you need to to is make Client extend UnicastRemoteObject.
There is an excellent RMI Trail as part of the Oracle/Sun Java Tutorials.

Related

How to get the "host" object java [duplicate]

This question already has answers here:
How to access an object's parent object in Java?
(4 answers)
Closed 3 years ago.
I need to get the "host" object of another object. Here an example
Suppose I have the
class Peer {
final Service service;
public Peer(Service service) {
this.service = service;
}
public void ping() {
service.ping();
}
}
and
class Service {
public String hola() {
this.getPeer()? //HERE I NEED SOME COMMAND TO GET the Peer object, is this possible?
}
}
Then in Service class, I need to get the peer object. Is this possible?
You need to store the Peer object inside your Service object while creating an instance of Service, that's the only way for retrieving it.
Create inside Service a constructor which accepts Peer
class Service {
Peer peer;
public Service(Peer peer){
this.peer = peer;
}
public Peer getPeer() {
return peer;
}
public String hola() {
Peer peer = getPeer();
}
}
Thing is:
this.getPeer()? //HERE I NEED SOME COMMAND TO GET the Peer object, is this possible?
There is no "command" to do that. Simply spoken: java objects have no notion of the context they were created in. The only way for an instance of Service to know about the Peer that created it: for example by passing a reference to that Service object, for example within its constructor. Or by having a setter method on the Service class that allows you to do that.
But of course: that sounds like the wrong way. A service instance should be really independent of the Peers using it! In other words: if it really makes sense that a Service knows one (or multiple?!) peers, then the Service class needs a field to "remember" that relationship. But at least the example given here rather suggests that your Service should not at all know (or care) about the Peers using it!

How to properly identify and hold client references in RMI callback?

I have a server and several "clients" (servers actually because of callbacks). A client can send a message to another only through the server. For this, the server must:
Identify the calling client.
Hold the clients' information and exported object reference so it is able to lookup the recipient.
I have read on the Remote Session pattern (1, 2, 3) and here and here, but I couldn't find the answer I was looking for.
For (1), I see the following options:
The client sends its exported object reference during the call to the server.
The client sends some identification information during the call to the server.
The client is identified with getClientHost.
The recipient must be sent as some identification information since clients do not hold a reference to each other.
public interface RemoteClient extends Remote {
void message(String sender, String message);
}
public interface RemoteServer extends Remote {
void relayMessage(String recipient, RemoteClient sender, String msg);
// or some identifier? // or string/identifier?
}
public class RemoteServerImpl extends UnicastRemoteObject implements RemoteServer {
void relayMessage(String recipient, RemoteClient sender, String msg) {
RemoteClient recp = lookup(recipient); // See point 2 below
String sndr = getRepresentation(sender); // See below...
recp.message(sndr, msg);
// OR using
String sndr = getRepresentation(getClientHost());
// Then sender parameter is not needed
}
}
I'm pretty sure getClientHost is not a reliable way of identifying the caller because it can disconnect and reconnect with a different IP, and I'm not sure if there are 2 computer in the same LAN that this method will be able to distinguish between them.
For (2), the options I see are:
Keep a Map of the identification information and the clients' exported objects (as mentioned, but not recommended, in one of the above answers).
Keep a Set of client information objects where these objects hold the remote object reference and whatever relevant information.
These are updated during login (registration) and logout.
Then lookup takes the information and returns the remote object reference and getRepresentation is similar to a reverse lookup.
My problem is not to make it work (it's working), it's to make it work correctly. Is there any advantage or preferred way from the above or otherwise?
You don't appear to have understood the remote session pattern at all. The session object the client is calling remote methods on is unique to the client, so there is no necessity for it to further identify itself during subsequent calls to the session. Whatever information the client sent to the login object to obtain the session can be stored in the session object, or the server can assign a unique client ID itself. The session object should also contain the callback of course: it's the only sensible place to put it.

Enum for error codes

I'm writting a client-server application. Now I'm just doing error handling. If a error occurs at the server (after a request from the client), the server should create an error object, serialize it and send it to the client.
In the error object I have a field for the error code. Of course I could place an enum in this object listing all possible error I want to cover, but then the serialized object gets to big.
Thus, I'm thinking about writting an external enum class available for the server and the client and only sending the error code from the server to the client.
Would this be a good idea?
If the client receives such an error code how can he then look up the corresponding enum in the enum class?
Just send the error code that you defined in your enum:
public enum ErrorCode {
OK(0), KO(1);
// ...
}
public class Error implements Serializable {
private int code;
private String message;
// ...
}
public void myMethod() {
// ...
// something went bad!
InternalError ie = getMyInternalErrorSomehow();
Error error = new Error(ie.getErrorCode().getCode(),
"something bad happened and i am sending you a nice message");
client.send(error);
}

Vaadin 7 send data from client to server

I am struggling to apprehend the way client and server communicate in vaadin 7. I have understood it well when it comes to server->client communication, but I still I cannot grasp how it works the other way round, namely from client to server. I have read that on the client side one should not change the State, but they should send the relevant data through rpc. On the client side there is a MyComponentClientRpc inteface which contains some methods and the developer should implement them in the Connector class. Then one can instantiate the inteface as anonymous class on server and can call the method. However in case one has a String "myString" on client how can it be transfered to the server, since one can only call the method on server passing the parameters on the time of calling it? How can then one get the data lying on client?
public interface MyComponentClientRpc extends ClientRpc {
public void getString(String s);
}
public class MyComponentConnector extends AbstractComponentConnector {
#SuppressWarnings("serial")
MyComponentClientRpc mcRpc = new MyComponentClientRpc() {
public void getString(String s) {
// TODO Do something useful
????
}
};
public MyComponentConnector() {
registerRpc(MyComponentClientRpc.class, mcRpc);
String a = "myString";
....
}
In particular, how does one implement the method on client, how does one insert the data in it and how should it be called on the server?
However in case one has a String "myString" on client how can it be
transfered to the server, since one can only call the method on server
passing the parameters on the time of calling it? How can then one get
the data lying on client?
You are using the wrong mechanism. There are actually two RPCs, ClientRpc and ServerRpc. In your example, you use ClientRpc, which is for calls from the server to the client.
To communicate from the client to the server, you have to extend from ServerRpc.
In the client, you call
MyComponentClientRpc rpc = RpcProxy.create(MyComponentClientRpc.class, this)
in order to get the proxy.

Restlet modify inbound object and return it or return new copy?

I'm trying to learn about restlets more coming from a soap-rpc background. One thing that I can't seem to figure out how to do (and maybe isn't possible) is to modify objects on requests or send back a copied version with modifications.
I'm trying to do something like this:
public interface AddressService {
#Get
Address addOnZipCode( Address address );
}
The server would be deployed out with the implementation and the client could make use of dynamic proxies to do its work.
The server starts just fine but when the client makes the call there is no indication on the server that the implementing method is being called. Furthermore, the client doesn't error until the call to the server returns - the returned object is null?!?
Is what I'm trying to do here possible with restlets? If so, any ideas on what I might be doing wrong?
I can post more code if necessary.
Thanks in advance.
EDIT #1:
I've even tried simplifying it to not use custom objects:
#Post
String execute( String message );
I get the following:
INFO: Starting the default HTTP client
Exception in thread "main" Method Not Allowed (405) - Method Not Allowed
at org.restlet.resource.ClientResource$1.invoke(ClientResource.java:1615)
at $Proxy5.execute(Unknown Source)
I'm beginning to think this is not possible, thus, I have a hard time seeing how this is a viable alternative to SOAP+RPC web services.
EDIT #2:
It looks like this is possible based on examples in the book: "Restlets in Action"
public interface AccountsResource {
#Get("txt")
public String represent();
#Post("txt")
public String add(String account);
}
EDIT #3:
It turns out that simply hitting the "stop" button in my Eclipse console was not shutting down the server instance. Opening a browser to the server URL showed some fishy results - there were multiple old server instances running. After shutting them all down I got it to work. Ultimately the answer was to use #Post instead of #Get1.
Have a look at this:
http://wiki.restlet.org/docs_2.0/13-restlet/21-restlet/318-restlet/303-restlet.html
On the server side your implementation must be like so:
class AddressServerResource extends ServerResource implements AddressService {
// implementation
}
On the client side:
ClientResource cr = new ClientResource("http://your-api.com/address/123");
AddressService service = cr.wrap(AddressService.class);
// use the service transparently
Edit:
#Post
Address addOnZipCode( Address address );

Categories