Remotor howto
From Wiki@comjat.net
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:
- RemotorClient: The client
- RemotorServer: The server
- BlueprintWebService: The Web-Service definition
- 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
