Intro
RemObjects SDK is an awesome framework. Not only because its development feeds me and my 10 cats ;) but also because it allows to efficiently create applications on literally a dozen different platforms (to name them: Win32 (a.k.a. Delphi apps), desktop .NET framework, Windows Phone, Silverlight, Mono, Android, Android via MonoAndroid, iOS, iOS via MonoTouch, native MacOS X apps, Java and JavaScript). Unfortunately, it takes a bit of time to get started with developing, you can’t just jump right in. I myself (and most of the software developers I know) usually want to try their new, just downloaded toy without reading a ton of documentation pages.
So this article will try to be a kind of “crash course”. It won’t go very deep (there are Wiki articles on just about anything, if you want to delve in deeper, and our support team will help you with any questions), it will just try to give you some ‘feeling’ for the tech, a foothold to conquer the new framework.
This article covers the .NET version of the RemObjects SDK, however, the main approach will remain the same for all platforms (.NET, Xcode, Delphi, Java, JavaScript), so you won’t waste your time with this article, even if you don’t use .NET in your development work.
My colleagues will provide similar articles devoted to the other supported platforms later this month.
Note: This article covers the ‘Client Calls The Server’ scenario and doesn’t touch the ‘Server Pings The Client’ one. At this moment it is enough to know that the latter scenario is possible as well and its implementation won’t affect the former one.
The Core
The very heart of the RemObjects SDK is based on Channels, Messages, Envelopes and Services. Two more components used under the hood are Interfaces and Invokers. Don’t be afraid of these words, things are actually simpler than they look.
Let’s take a look at the ‘Client calls the server method’ scenario:
var methodCallResult = sampleService.DoSomething();
Looks simple, doesn’t it?
Here’s what happens from the RemObjects SDK’s point of view:
The client code calls the Foo method of some object serverService. Here, sampleService is an instance of the auto-generated proxy class, so calling the sampleService.DoSomething() method actually means the following:
-
Sending out a request
The Interface class serializes the request to the server using the configured Message. The Message is then wrapped by the attached Envelopes, if any are set up (f.e. data is encrypted somehow). Then the resulting data package is sent over the wire by the Client Channel. -
Processing the request on the server side
The Server Channel retrieves the data package and pushes it to the Message component. Message unwraps it using the attached Envelopes, if needed. The resulting data package is provided to the Invoker, which reads the requested service, method names and method parameters from it.
Then the Service method is invoked, which does the actual job.
After the service method has finished, its result (or any exceptions occurred) are serialized, wrapped, and sent back over the wire using the Server Channel (yes, the processing pipeline is similar to step 1).
- Retrieving the response
The Client Channel retrieves the data package, unwraps it and provides the serialized data package to the Interface‘s method that initiated the remote service call. This method then deserializes the provided data package and provides the method result back to the user code.
The magic is that the entire process is transparent for the user code on both ends of the wire and doesn’t require much attention from the developer. While the service method call pipeline might seem complicated (or even over-complicated), its performance overhead is negligible (especially for the binary serialization-based Message), making the actual network speed the main issue to care about, not the inner RemObjects SDK processes.
Sample Server
Let’s create a sample server and client app in a step-by-step guide and see how the Channels, Messages, Envelopes, Interfaces, Invokers and Services entities are implemented in a real RemObjects SDK-based application.
Prerequisites
For this tutorial you’ll need Visual Studio and either the RemObjects SDK for .NET or the Data Abstract for .NET frameworks installed. Also, a minimal knowledge of C# is required, but the same concepts would apply to Visual Basic for .NET, Oxygene or any other .NET language.
Server Application
As mentioned above, the server application should contain the Server Channel, Message, Envelope (if needed) and Invoker, as well as the Service itself.
- Create a new Class Library project named BusinessLogic and add a single class to it:
using System; namespace BusinessLogic { public class StringProcessor { public Int32 Process (String value) { if (String.IsNullOrEmpty(value)) return -1; return (value + @"Secret Salt").GetHashCode(); } } }
This project imitates an already existing implemetation of a real-world business logic method that should be exposed to remote client applications. While it is not necessary to put the business logic into a separate project, it was done here to emphasize that the business logic layer is fully separated from the to-be-implemented communication layer.
-
Add a new Windows Forms project to the solution and name it SampleServer. Of course, a real-world server application should be running as a Windows Service (and RemObjects SDK provides the needed application template), but for this “crash course” it will be easier to use the simple Windows Forms project.
-
Add the following references to the SampleServer project, as they provide the needed classes:
- RemObjects.InternetPack.dll
- RemObjects.SDK.dll
- RemObjects.SDK.Server.dll
- RemObjects.SDK.ZLib.dll
Also add a reference to the previously created BusinessLogic project, as we will expose its methods in the server.
- Now we need to define the Server Surface Area, i.e. the service name, its methods, their parameters and return values, etc. This step is needed to allow the RemObjects SDK to generate strongly-typed Interface and Invoker classes.
Add a new ‘RemObjects SDK Service Definition’ item to the project.
A new file named NewLibrary1.RODL will be added to the project and the Service Builder app will be started automatically. The RODL is an XML file containing the service definition written in the ‘RemObjects Definition Language’ (or RODL), and the Service Builder provides the GUI to edit .RODL files without diving into the definition language syntax. Please note that the RemObjects SDK exposes the object model that allows to load, parse and generate .RODL files (and even to generate code based on these files), but this topic is outside the scope of this article.
As you can see, the file is almost empty. Select the single topic in the tree on the left side of the app’s window. The edit boxes that will appear will allow you to change the library name, set its namespace or even write its definition. Set the Library name to BusinessLogicLibrary.
A Library contains all the entities in the given RODL file. Consider it as a root of a service definition tree.
The next step is to define the Service. Press the ‘Service’ button (or right-click the Library name in the tree and issue the ‘Service’ context menu command) and rename the just added Service to BusinessLogicService.
Now we can define this service’s methods. Press the ‘Add Operation’ button (the one with the green “+” glyph). A new method named ‘NewMethod’ will be added. Double-click its definition and change the method’s name to Process.
The last step is to define this method’s parameters and set its return type. Press the ‘Add Parameter’ button and change the parameter’s name to ‘value’. Change the parameter’s type to Utf8String (the ‘Data Type’ editor allows to select a type name from a combo box, so there is no need to enter it manually).
Set the Result type to Integer (the type name can be selected from the combo box as well).
If everything was done right, the Service Builder window should now look like this:
Close the Service Builder.
Set the ‘Build Action’ of the just added RODL file to ‘Embedded Resource’, so it will be embedded into the compiled application. You will find out why this is needed below.
- As you can see, once you closed the Service Builder, three new code file were added to your project: BusinessLogicLibrary_Intf.cs, BusinessLogicLibrary_Invk.cs and BusinessLogicService_Impl.cs.
BusinessLogicLibrary_Intf.cs contains the Interface classes, BusinessLogicLibrary_Invk.cs contains the set of Invoker classes and BusinessLogicService_Impl.cs is a blank Service implementation class.
There is no need (and it is NOT recommended) to edit the code in the first two files.
In case you change the RODL file (for example add one more method to the service definition), the *_Intf and *_Invk files will be regenerated automatically to reflect the RODL changes. At the same time the _Impl file will stay untouched to prevent any code loss. Since the service implementation class contained in the _Impl file implements the service interface contained in the _Intf file, the service project won’t be able to build until the Service implementation class will match the Service definition.
For example: If you add a method to a service definition, the interface representing this service in the _Intf file will contain the new method definition as well. And because the Service implementation class has to implement this interface, the developer will have to add the corresponding method implementation to the service definition class.
- Open the Service Implementation file (i.e. BusinessLogicService_Impl.cs) and find the Process method implementation. It is empty, so change it to
public virtual int Process(string value) { return (new BusinessLogic.StringProcessor()).Process(value); }
At this time, we have defined and implemented a Service (and it probably took more time for you to read the text above than to actually execute the steps in Visual Studio). Now we need to make the service available remotely.
- Below, we will manually implement the network connectivity layer using the RemObjects SDK components. This is done to demonstrate the ins and outs of the framework as the RemObjects SDK provides the application templates and (starting from the May 2013 release) the pre-implemented NetworkServer class that allows to implement the connectivity layer in literally 3 lines of code.
Open the main form in designer mode (remember that our server app is intentionally primitive, so no properly layered architecture or ‘Run As Windows Service’ support here):
- Add the IpHttpServerChannel and BinMessage components to the main form (note that a licenses.licx file was automatically added to the server app project. Please do not delete this file).
- Open the properties of the IpHttpServerChannel component and open the custom editor for the Dispatchers property.
- Press the ‘Add’ button and select the previously added BinMessage instance as a value for the Message property.
- Press OK to close the dialog and save the property’s value.
That’s it. The server message processing pipeline (i.e. the Server Channel and Server Message) is set up.
- Open the code-behind file of the main form and add this code line to the very end of the main form class constructor:
this.ipHttpServerChannel1.Open();
This code line will ‘open’ the server channel so it will start to accept the incoming connections.
Now you can start the server app. Open a web browser and go to the page http://localhost:8099/rodl. The server app will show the contents of the embedded RODL file at this address. Note that you can always set the the ‘Embedded Resource’ property to None for the RODL file and the server will not advertise its methods.
Client Application
Even the most advanced server application is useless if there is no client application for it. So… let’s create one!
If you remember, a client application needs the following components implemented: Channel, Message, (possibly) Envelope and Interface. This may look scary again, but it will be much easier than it sounds.
-
Create a new Console Application (for simplicity reasons).
-
Add references to the assemblies
- RemObjects.InternetPack.dll
- RemObjects.SDK.dll
- RemObjects.SDK.ZLib.dll
These assemblies provide the client connectivity classes (as you can see, there is no need to reference the RemObjects.SDK.Server.dll assembly).
-
Now issue the ‘Add’ -> ‘Existing Item’ command (available via the context menu for the client application project entry in the Solution Explorer).
-
Select the BusinessLogicLibrary_Intf.cs file (it can be found in the server’s project folder) and add it as a link.
Adding the _Intf file as a link has a little but important advantage over simply adding an existing file to the client app project: when the server RODL is changed, the _Intf file is automatically regenerated. If this file is added as a link, there will be no need to update the _Intf file copy contained in the client app project.
This step adds the Interface part of the client app.
- Add the following code to the Program.cs:
using System; namespace SampleConsoleApplication { class Program { static void Main(string[] args) { Console.Write("Enter string: "); String value = Console.ReadLine(); Console.WriteLine(); Console.WriteLine("Processing..."); // Communicating with the server SampleServer.IBusinessLogicService service = new SampleServer.BusinessLogicService_Proxy(@"http://localhost:8099/bin"); Int32 hash = service.Process(value); Console.WriteLine(); Console.WriteLine("String entered: {0}", value); Console.WriteLine("String hash: {0}", hash); Console.WriteLine("Press ENTER to continue..."); Console.ReadLine(); } } }
The Console.WriteLine and Console.ReadLine calls here are pretty usual for any console application. Let’s take a closer look at these code lines:
// Communicating with the server SampleServer.IBusinessLogicService service = new SampleServer.BusinessLogicService_Proxy(@"http://localhost:8099/bin"); Int32 hash = service.Process(value);
Here, a proxy for a server app service is created and then a service method is called (the Channel and Message components are automatically created behind the scenes to make this possible).
The IBusinessLogicService interface lists all methods exposed by the remote service, while the CoBusinessLogicService.Create static method creates a proxy class that implements this interface. This makes it possible to perform calls to a remote server transparently for the client app’s code.
- There are probably some explanations needed regarding where the “http://localhost:8099/bin” URL came from. Let’s take a closer look on each part of the URL:
- http – Protocol used by the server channel. In this sample, we use a simple HTTP channel. The RemObjects SDK supports several other communication protocols as well (f.e. TCP-based ones).
- localhost – Name or IP address of the server’s host.
- 8099 – Port the Server Channel is listening to. 8099 is the default port for the IpHttpServerChannel used in our server application.
- bin – So-called dispatcher name. This is the name of the Message on the server side that processes the incoming data package. bin is the default name for the BinMessage.
- Build and start both the server and client applications. Enter any string into the client application window and see the result returned by the server app:
Conclusion
At this point you should have a basic understanding of how the RemObjecs SDK-based communications can be implemented. As an exercise, try to add one more method to the Service and call it.
This article doesn’t cover many (or even most) of the RemObjecs SDK features. Server Events, Envelopes, client authentication processing and SSL support were not covered for simplicity reasons. Search our Wiki for the questions you are interested in. If for some reason you can’t find the info you need, don’t hesitate to call on our support via RemObjects Connect or email to support@remobjects.com.