Remotor howto

From Wiki@comjat.net

Jump to: navigation, search

Contents

Introduction

As you have consumed some theory by now, it is time to show you an example. In this howto we use a client class, a server class and a webservice. The webservice needs an interface definition in order to be published - for usage by your remote client. The whole example fits into three(!) classes and one(!) interface - that is lightweight!

Author

Siegfried Steiner User:steiner

Remotor: Client, server, webservices

As mentioned above, we need an interface defining the webservice to be published by the server. This interface defines the methods to be used by the client. The classes for this howto reside in the package <net.comjat.tutorial.remotor>.

Important: For the example you need the COMJAT.NET Libraries found here. Get the Remotor example source code here.

These are the classes:

  1. RemotorClient: The client
  2. RemotorServer: The server
  3. BlueprintWebService: The Web-Service definition
  4. WebService: The Web-Service implementation

Now we will have a look at code snippets of the classes above:

The WebService class

The Web-Service now implements our interface which defines all the methods needed by the client:

public class WebService implements BlueprintWebService {

Figure 2: The interface defining our methods of the webservice is being implemented

It implements the following Methods:

void pushMessage( String aMessage );
String[] getMessageArray();

Figure 3: The methods of our webservice

'pushMessage' is used by the client to push some message. The webservice will log the messages on the screen.

The RemotorServer class

First of all we need some server which publishes our webservice. We use the RemotorServer class for this purpose. The Remotor-System gives us full control of how we establish a data stream between the remote systems to communicate. In this example we use sockets. Lets look at the code:

ServerSocket theServerSocket = new ServerSocket( 5157 );
...
Socket socket = theServerSocket.accept();

Figure 4: Using sockets to establish the connection between the client and the server

Here we are waiting for someone to connect. When the client attaches, then a socket is created. From the socket we are able to extract input streams and output streams:

OutputStream out = theSocket.getOutputStream();
InputStream in = theSocket.getInputStream();

Figure 5: Retrieving our streams from the socket

Now it is time to create an instance of the Remotor-System handling the method invocation requests if some client calls:

BlueprintProviderRemotor theProviderRemotor = new IoStreamProviderRemotor(in, out, null, _logPublic);
theProviderRemotor.startConnection();

Figure 6: Preparation for handling client requests on the server

We created an instance of a special flavour of the Remotor-System, the IoStreamProviderRemotor, and we started the connection (don't forget to do so in your programms!). Actually the Remotor-System is not restricted in using streams! The IoStreamProviderRemotor implementation makes the use of streams much easier!

Last but not least we publish the Web-Service. Using the Remotor-System, only objects are accessible as web-services, which have actively been published by you!

theProviderRemotor.publishObject( new WebService(_logPublic) );

Figure 7: The server is publishing our webservice

In comparison, there are webservice systems such as Flash Remoting MX, which will instantiate just about any class being pointed at by the client and which is defined public on the server, thereby enabling big security holes in your server code!

To get an overview, here is the main loop of the server-code:

public RemotorServer() {
  try {
    ServerSocket theServerSocket = new ServerSocket( 5157 );
    while ( true ) {
      _logPublic.log( "SERVER: Waiting for connection ..." );
      Socket theSocket = theServerSocket.accept();
      _logPublic.log( "SERVER: Connected!" );
      OutputStream out = theSocket.getOutputStream();
      InputStream in = theSocket.getInputStream();
      BlueprintProviderRemotor theProviderRemotor = new
        IoStreamProviderRemotor(in, out, null, _logPublic);
      theProviderRemotor.startConnection();
      _logPublic.log( "SERVER: Publishing Web-Service  ." );
      theProviderRemotor.publishObject( new WebService(_logPublic) );
      _logPublic.log("SERVER: Web-Service published!" );
    }
  }
  catch ( IOException ioe ) {
    ioe.printStackTrace();
  }
  catch ( ConnectionUnpredictableException cue ) {
    cue.printStackTrace();
  }
  catch ( VetoUnpredictableException vuex ) {
    vuex.printStackTrace();
  }
}

Figure 8: THe main loop of the server code

The RemotorClient class

Now we make use of some client which consumes (uses) our webservice. We take the RemotorServer class for this purpose. As we use sockets to establish a connection between the client and the server, here is the code to do so:

Socket theSocket = new Socket( "localhost", 5157 );

Figure 9: Creating the socket from the client

Attention: In order to connect successfully when testing, your server must be started before the client. Now we have a socket. From this socket we are able to extract input streams and output streams:

OutputStream out = theSocket.getOutputStream();
InputStream in = theSocket.getInputStream();

Figure 10: Retrieving the streams from the socket

In order to consume the Web-Service, we start create an instance of the Remotor-System and start the connection:

BlueprintConsumerRemotor theConsumerRemotor = new IoStreamConsumerRemotor(in, out, null, _logPublic );
theConsumerRemotor.startConnection();

Figure 11: Starting the webservice consumer

In order to get the Web-Service, we have different options: We may register an event listener so that we get notified when a new object is being published by the server's Remotor-System, or we periodically take a look into the list of objects being published by the server.

I have chosen to use the event driven way: This way we get notified when the Web-Service is accessible and we do not need to loop until we detect our Web-Service in the client's Remotor-System. Take a look at the Javadoc in order to find out how to look at the list of accessible objects in the cleint's Remotor-System.

Here is the code on how to register our event listener:

theConsumerRemotor.getEventObservablePublic().addEventListener ( this );

Figure 12: Getting informed when something interesting happens

Our RemotorClient class implements an interface called GenericEventListener which defines a method called 'pushEvent()'. When adding the RemotorClient instance to the observable, then the observable expects an object of type GenericEventListener. Therefore we implement this interface:

public class RemotorClient implements GenericEventListener,
  GenericVetoableListener {
  public void pushEvent( GenericEvent evt ) {
    if ( evt instanceof BlueprintProxyCreatedRemotorEvent ) {
      BlueprintProxyCreatedRemotorEvent proxyEvent =
        ( BlueprintProxyCreatedRemotorEvent ) evt;
      Object theProxy = proxyEvent.getProxyDescriptor().getProxy();
      if ( theProxy instanceof BlueprintWebService ) {
        _webService = (BlueprintWebService) theProxy;
      }
    }
  }
}

Figure 13: Handling the events coming from the webservice server

When the event is forwarded to our 'pushEvent()' method, we are able to get the proxy for the Web-Service and store it an a global variable. In Java, a proxy is an object imitating some other object: The object behaves as if it were an object implementing interfaces of your own choice. The Remotor-System creates proxy objects behaving as if they implement the interfaces being used by the classes which are published by the server. From an interface point of view, you will not see any difference between the object and its proxy!

As we have retrieved our Web-Service now, we can access it an call its methods. Take in mind that the methods we call are executed on the server! Therefore when you define your own Web-Services make sure that the arguments to be passed to the remote methods are serializable! Else the arguments will not make it to the server and you get some not-serializable exception! Some data types are automatically converted by the Remotor-System internally by COMJAT's customized streams (such as Iterator-Objects and primitive types). That's the way you do it - calling the Web-Service's methods:

_webService.pushMessage("Hello Server!");
_webService.pushMessage("How Are you?");
String[] theMessageArray = _webService.getMessageArray();

Figure 14: Using the webservice from the server on the client

It is straight forward. Handle the Web-Service as it were an object of type BlueprintWebService!

To finish it off, we close our connections:

theConsumerRemotor.closeConnection();
theSocket.close();

Figure 15: Finally closing the connection on the client

Here is the main code for the RemotorClient:

public RemotorClient() {
  try {
    _logPublic.log( "CLIENT: Connecting ...");
    Socket theSocket = new Socket( "localhost", 5157 );
    InputStream in = theSocket.getInputStream();
    OutputStream out = theSocket.getOutputStream();
    _logPublic.log( "CLIENT: Connected!");
    BlueprintConsumerRemotor theConsumerRemotor =
      new IoStreamConsumerRemotor(in, out, null, _logPublic );
    theConsumerRemotor.startConnection();
    theConsumerRemotor.getEventObservablePublic().
      addEventListener( this );
    theConsumerRemotor.getVetoableObservablePublic.
      addVetoableListener( this );
    while(_webService == null) {
      try {
        Thread.sleep(500);
      }
      catch (InterruptedException ex) { }
    }
    _logPublic.log( "CLIENT: Calling Web-Service  .");
    _webService.pushMessage("Hello Server!");
    _webService.pushMessage("How Are you?");
    String[] theMessageArray = _webService.getMessageArray();
    for (int i = 0; i < theMessageArray.length; i++) {
      _logPublic.log( "CLIENT: Retrieved from SERVER - " +
        "message[" + i + "] = " + theMessageArray[i] );
    }
    theConsumerRemotor.closeConnection();
    theSocket.close();
  }
  catch ( IOException ioe ) {
    ioe.printStackTrace();
  }
  catch ( ConnectionUnpredictableException cue ) {
    cue.printStackTrace();
  }
}

Figire 16: Here is the code for the client

Links

Personal tools