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.
Related
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.
I am developing an Android app using GAE on Eclipse.
On one of the EndPoint classes I have a method which returns a "Bla"-type object:
public Bla foo()
{
return new Bla();
}
This "Bla" object holds a "Bla2"-type object:
public class Bla {
private Bla2 bla = new Bla2();
public Bla2 getBla() {
return bla;
}
public void setBla(Bla2 bla) {
this.bla = bla;
}
}
Now, my problem is I cant access the "Bla2" class from the client side. (Even the method "getBla()" doesn't exist)
I managed to trick it by creating a second method on the EndPoint class which return a "Bla2" object:
public Bla2 foo2()
{
return new Bla2();
}
Now I can use the "Bla2" class on the client side, but the "Bla.getBla()" method still doesn't exit. Is there a right way to do it?
This isn't the 'right' way, but keep in mind that just because you are using endpoints, you don't have to stick to the endpoints way of doing things for all of your entities.
Like you, I'm using GAE/J and cloud endpoints and have an ANdroid client. It's great running Java on both the client and the server because I can share code between all my projects.
Some of my entities are communicated and shared the normal 'endpoints way', as you are doing. But for other entities I still use JSON, but just stick them in a string, send them through a generic endpoint, and deserialize them on the other side, which is easy because the entity class is in the shared code.
This allows me to send 50 different entity types through a single endpoint, and it makes it easy for me to customize the JSON serializing/deserializing for those entities.
Of course, this solution gets you in trouble if decide to add an iOS or Web (unless you use GWT) client, but maybe that isn't important to you.
(edit - added some impl. detail)
Serializing your java objects (or entities) to/from JSON is very easy, but the details depend on the JSON library you use. Endpoints can use either Jackson or GSON on the client. But for my own JSON'ing I used json.org which is built-into Android and was easy to download and add to my GAE project.
Here's a tutorial that someone just published:
http://www.survivingwithandroid.com/2013/10/android-json-tutorial-create-and-parse.html
Then I added an endpoint like this:
#ApiMethod(name = "sendData")
public void sendData( #Named("clientId") String clientId, String jsonObject )
(or something with a class that includes a List of String's so you can send multiple entities in one request.)
And put an element into your JSON which tells the server which entity the JSON should be de serialized into.
Try using #ApiResourceProperty on the field.
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.
I was wondering how people with more experience and more complex projects get along with this "uglyness" in the REST Communication. Imagine the following Problem:
We'll need a fair amount of functionalities for one specific resource within our REST Infrastructure, in my case that's about 50+ functions that result in different querys and different responses. I tried to think of a meaningful resource-tree and assigned these to methods that will do "stuff". Afterwards, the Server Resource Class looks like this:
#Path("/thisResource")
public class SomeResource {
#GET/POST/PUT/DELETE
#Path("meaningfulPath")
public Response resourceFunction1 ( ...lots of Params) {
... logic ....
}
//
// lots of functions ...
//
#GET/POST/PUT/DELETE
#Path("meaningfulPath")
public Response resourceFunctionN ( ...lots of Params) {
... logic ....
}
}
To construct the urls my client will call, I made a little function to prevent Typos and to take better use of Constants
so my Client looks like this:
public class Client() {
public returnType function1 () {
client.resource = ResourceClass.build(Constants.Resouce, "meaningfulPath");
...
return response.getEntity(returnType);
}
}
Now the questions that bothers me is how could I link the client function and the server function better?
The only connection between these two blocks of code is the URL that will be called by the client and mapped by the server, and if even this URL is generated somewhere else, this leads to a lot of confusion.
When one of my colleagues needs to get into this code, he has a hard time figuring out which of the 50+ client functions leads to wich server function. Also it is hard to determine if there are obsolete functions in the code, etc. I guess most of you know about the problems of unclean code better than I do.
How do you deal with this? How would you keep this code clean, maintainable and georgeous?
Normally, this would be addressed by EJB or similar technologies.
Or at least by "real" web services, which would provide at least WSDL and schemas (with kind of mapping to Java interfaces, or "ports").
But REST communication is very loosely typed and loosely structured.
The only thing I can think of now, is: define a project (let's call it "Definitions") which would be referenced (hence known) by client and server. In this project you could define a class with a lot of public static final String, such as:
public static final String SOME_METHOD_NAME = "/someMethodName";
public static final String SOME_OTHER_METHOD_NAME = "/someOtherMethodName";
Note: a static final String can very well be referenced by an annotation (in that case it is considered to be constant by the compiler). So use the "constants" to annotate your #Path, such as:
#Path(Definitions.SOME_METHOD_NAME)
Same for the client:
ResourceClass.build(Constants.Resouce, Definitions.SOME_METHOD_NAME);
You are missing the idea behind REST. What you are doing is not REST but RPC over HTTP. Generally you are not supposed to construct URLs using out of band knowledge. Instead you should be following links received in the responses received from the server. Read about HATEOAS:
http://en.wikipedia.org/wiki/HATEOAS
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 );