Pass Remote object in method to RMI server? - java

I have an RMI client that connects to some RMI server just to let it know it can use this new client.
Can I pass directly some Remote object so that:
serverRemoteObject.registerClient(theClientRemoteObjectTheServerShouldUse);
will actually give the server some object he can use without connecting to my client?
The following question says it is possible, but no real example was given:
Is it possible to use RMI bidirectional between two classes?
Andrew

Yes, you can. This is how exactly callbacks work in case of RMI. You send across an object to the server and when the server invokes a method on your object, it would be executed in the "client" JVM as opposed to on the server. Look into UnicastRemoteObject.export method for export any object which implements the Remote interface as a remote object which can be passed to your server.
interface UpdateListener extends Remote {
public void handleUpdate(Object update) throws RemoteException;
}
class UpdateListenerImpl implements UpdateListener {
public void handleUpdate(Object update) throws RemoteException {
// do something
}
}
//somewhere in your client code
final UpdateListener listener = new UpdateListenerImpl();
UnicastRemoteObject.export(listener);

Related

How can I create multiple servers with Java RMI?

I need:
A client, which communicates with the front-end, which communicates with 3 file servers.
How should I go about doing this? It needs to use RMI as distributed systems.
I also need to monitor all three file servers.
From what I understand, I need to establish an RMI registry, but how do I establish three concurrent servers within one registry?
Okay, so am I right in thinking i'd have the following: A server interface, a server implementation, and a master server which creates the three servers (with unique names) and finally a client?
The 'master server' needs to create a Registry on its own localhost, bind itself to the Registry so the slave servers can find it, and export a remote interface that lets the servers register themselves with it.
The master server must do the binding to this Registry on behalf of the slaves, as you can't bind to a remote Registry. But in fact the slaves don't need to be bound to the Registry at all, only registered with the master.
The master needs to export a second remote interface that provides the API to the client, which provides the upload API and whose implementation performs the balancing act. I would keep this interface separate from the interface used by the slaves, both for security reasons and for simplicity: you don't need clients trying to be slaves, or worrying about what the slave-relevant methods in the remote interface are.
All these servers and registries can run on port 1099.
The slaves are presumably multiple instances of the same service, so they all use a common remote interface. This interface provides the upload-to-slave API, and it also needs to allow each slave to provide the knowledge about how full each slave is, possibly as a return value from the upload method, or else as a query method.
Quick sketch:
public interface UploadMaster extends Remote
{
void upload(String name, byte[] contents) throws IOException, RemoteException;
}
public interface LoadBalancingMaster extends Remote
{
void register(Slave slave) throws RemoteException;
void unregister(Slave slave) throws RemoteException;
}
public interface Slave extends Remote
{
/** #return the number of files now uploaded to this slave. */
int upload(String name, byte[] contents) throws IOException, RemoteException;
int getFileCount() throws RemoteException;
}
I hope this is homework. RMI is a poor choice for file transfer, as it bundles up the entire argument list into memory at both ends, rather than providing a streaming interface.

Understanding java RMI exportObject method

I'm very new to RMI and I just decided to give it a try. I got confused by the exportObject(Object, int) method. The documentation says:
Exports the remote object to make it available to receive incoming
calls, using the particular supplied port. The object is exported with
a server socket created using the RMISocketFactory class.
Consider the following simple example:
public interface Client extends Remote {
void clientMethod() throws RemoteException;
}
public class ClientImpl implements Client {
public clientMethod() throws RemoteException {
System.out.println("clientMethod invoked");
}
}
Client stub = (Client) UnicastRemoteObject
.exportObject(new ClientImpl(), 56789); //<------ HERE
So we create a stub and will transfer it to another VM either manually or through RmiRegistry, doesn't matter here.
I'm confused by "[...] the object is exported with a server socket [...]"
What do they mean by that?
A ServerSocket is created to listen for incoming connections at the port you specified when exporting. This port can be shared between multiple remote objects.
The statement about the RMISocketFactory is incorrect. Where did you read that? This class has been obsolete since 1998.
The stub contains the server's hostname or IP address and port number, and some internal data to identify the remote object it belongs to.
TCP connections between the stub and the remote object are created on demand when you call remote methods, via a connection pool.
So, when we transfer the stub to another VM (VM 0), the stub will hold a socket connection to the VM (VM 2) it was originally created on.
No, see above.
The VM 2 in turn will maintain a server socket to accept incoming method invocations.
Correct.

Persisting data in TCP server while client channel is connected

I'm building a TCP Server using Netty.
Is there any way to persist the connected client's session data while its channel exists?
for example, when a client connect to the server, I need to create its class instance and reuse in different ways when he send messages.
something like the code below:
// this is called when the client connect to the server
public void channelActive(final ChannelHandlerContext ctx) {
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
new GenericFutureListener<Future<Channel>>() {
public void operationComplete(Future<Channel> future) throws Exception {
// I need to create the class instance when the
// client connects to the server
ClientData clientData = new ClientData(ctx.channel());
channels.add(ctx.channel());
}
}
);
}
// this is called when the server receives a message from the connected client
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
if("update".equals(msg)){
// then I need the retrieve the data created
// in the ChannelActive method.
clientData().update();
}
}
While browsing for solutions, I found a few examples where the developer used a cache service (like memcache or redis) to store and retrieve the data related to the connected client.
But I wish to solve this without depending on a external process.
Is there any way to achieve this? Any advice on the subject would be appreciated.
Thank you
You should use AttributeMap.attr(AttributeKey key), which is inherited by ChannelHandlerContext:
Storing stateful information
AttributeMap.attr(AttributeKey) allow you to store and access stateful information that is related with a handler and its context. Please refer to ChannelHandler to learn various recommended ways to manage stateful information. [1]
[1][http://netty.io/4.0/api/io/netty/channel/ChannelHandlerContext.html]

GWT: invoke the same RPC-methods on the server-side as on the client-side

The question might seem stupid/trivial and might be, but I simply cannot understand how to achieve my goal. (Sorry if the title is misguiding, couldn't think of a better one)
I have a webpage on a App Engine server which uses GWT. I got client code and server code. The client code can call RPC methods without any problem (my problem has nothing to do with the "gwt-client" at all).
I got the following classes:
//MyClassService.java - client package
#RemoteServiceRelativePath("myService")
public interface MyClassService extends RemoteService{
public doSomething();
}
//MyClassServiceAsync.java - client package
public interface MyClassServiceAsync{
public void doSomething(AsyncCallback<Void> callback);
}
//MyClassServiceImpl.java - server package
public class MyClassServiceImpl extends RemoteServiceServlet implements MyClassService{
#Override
public void doSomething()
{
//does something
}
}
A scenario and what I want to do:
I've got a remote client, in other words, a client who's not connecting through the page via the "GWT-interface", it's a client who's simply making GET, POST requests to a path on the server (from elsewhere). This remote client is not "using" GWT at all. The client is connecting through an HttpServlet, inside this servlet I want to reuse the RPC mechanics so that i don't have to rewrite the interfaces, who are on the client side and using client-dependent code (the implementation is already server-side).
To reuse the existing methods on the server-side I could create an instance of MyClassServiceImpl.java and just use those. BUT as you can see above, they are implemented as synchronous methods, since GWT-RPC automatically makes the calls asyncronous when using the GWT-RPC.
How would i go about to reuse the MyClassServiceImpl on the server-side and also get them as asynchronous?
Also if I'm wrong with the approach I'm taking, please suggest some other solution. For example, one solution might be for the remote client to directly communicate with the RemoteServiceServlet instead of creating a HttpServlet which the client connects through, but I don't know if that's possible (and if it is, please tell me how)!
Thank you!
EDIT (thanks to some answers below I got some insight and will try to improve my question):
The server-side implementation of the methods is SYNCHRONOUS. Meaning they will block until results a returned. When invoking these method from the gwt-client code, they are 'automatically' made ASYNCHRONOUS one can call them by doing the following:
MyClassServiceAsync = (MyClassServiceAsync) GWT.create(MyClassService.class);
ServiceDefTarget serviceDef = (ServiceDefTarget) service;
serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL() + "myService");
service.doSomething(new AsyncCallback<Void>() {
#Override
public void onSuccess(Void result) {
//do something when we know server has finished doing stuff
}
#Override
public void onFailure(Throwable caught) {
}
});
As you can see from the above code, there is support for the doSomething method to take an AsyncCallback, without even having the implementation for it. This is what I wanted on the server-side so i didn't have to use threads or create a new implementation for "async-usage". Sorry if I was unclear!
1) Any client can call MyClassServiceImpl.doSomething() with the current configuration. MyClassServiceImpl is a servlet and properly exposed. In order to achieve communication this way, the client must be able to "speak" the GWT dialect for data transportation. Google may provide you with libraries implementing this. I haven't used any, so I cannot make suggestions.
An example, proof-of-concept setup: Check the network communications with Firebug to get an idea of what is going on. Then try calling the service with curl.
2) If you do not want to use the GWT dialect, you can easily expose the same service as REST (JSON) or web services (SOAP). There are plenty of libraries, e.g. for the REST case RestEasy and Jersey. You do not mention any server-side frameworks (Spring? Guice? CDI?), so the example will be simplistic.
I'd suggest implementing your business method in a class independent of transportation method:
public class MyBusinessLogic {
public void doSomething() {
...
}
}
Then, the transport implementations use this business logic class, adding only transport-specific stuff (e.g. annotations):
GWT:
public class MyClassServiceImpl extends RemoteServiceServlet implements MyClassService{
#Override
public void doSomething() {
MyBusinessLogic bean = ... // get it from IoC, new, whatever
bean.doSomething();
}
}
JAX-RS:
#Path("myService")
public class MyResource {
#GET
public void doSomething() {
MyBusinessLogic bean = ... // get it from IoC, new, whatever
bean.doSomething();
}
}
So the transport endpoints are just shells for the real functionality, implemented in one place, the class MyBusinessLogic.
Is this a real example? Your method takes no arguments and returns no data.
Anyhow you can create a new servlet and invoke it via normal HTTP request. The servlet then just invokes the target method:
public class MyNewServlet extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response){
MyBusinessLogic bean = ... // get it from IoC, new, whatever
bean.doSomething();
}
}

RMI - create thread on server to serve client

I'm developing a application using rmi which allow client to login, perform some task and logout. I know that each client is considered as one thread when it call a method on server, however, all clients' threads call to the same object created on server. So now, I want to for each client login successfully, a new thread is created (and a new object, which is used by only one client, is binded, too), a thread terminates when client logout. Hence, each client has its own server's object to work with.
Thank you very much.
Cheers
I know that each client is considered
as one thread when it call a method on
server
That's not correct. The relationship between clients and server threads is undefined in RMI.
In any case you don't need a thread per client. You need a remote object per client. This is a job for the Session pattern:
public interface Login extends Remote
{
Session login(String credentials) throws RemoteException;
}
public interface Session extends Remote
{
// Your API here
}
Have your Login implementation object return a new Session implementation object for every client.

Categories

Resources