Delphi Clinic | C++Builder Gate | Training & Consultancy | Delphi Notes Weblog | Dr.Bob's Webshop |
![]() |
![]() |
![]() |
|
Delphi 6 and 7 Enterprise allow us to build SOAP server applications. But you do not need the Enterprise versions to build SOAP applications, since a new product called RemObjects SDK (from RemObjects Software) allows you to produce multi-tier and SOAP applications even in the Professional version of Delphi.
RemObjects SDK
RemObjects SDK is made by RemObjects Software, and gives us the ability to build multi-tier applications (including web services) that offer a number of benefits over the technology found in Delphi such as increased performance and scalability.
RemObjects SDK can be installed in Delphi 5, 6 or 7, but a Kylix version is in the works (or so I’ve been told).
This is very significant for Kylix developers who currently have only one communication protocol available when building multi-tier applications, namely SOAP.
And while SOAP is a standard and regarded by many (including Microsoft) as The Holy Grail as well as The Silver Bullet, it is by no means a high-performance standard.
In fact, the SOAP envelopes (containing platform and language independent information) that are being sent back and forth from the client to the server are simply XML documents that have to be generated, parsed, marshalled, etc.
for every request and response.
This process is even slower than CORBA, which at least sends the request in an ORB binary format over the wire, and much slower than (D)COM.
Like I stated earlier, RemObjects SDK offers a more efficient way to build multi-tier applications.
This is realised by the fact that RemObjects SDK supports both SOAP and a proprietary binary format as communication protocol.
And if you implement your RemObjects server as a Smart Service, you can export the server (and expose your classes) using SOAP where needed, and using BIN where possible.
Meaning that “regular” visitors can use the SOAP standard to communicate with your RemObjects server, but your special customers can use the much faster binary protocol.
And the fact that RemObjects SDK will also be available for Kylix is very good (performance) news for developers who want to place their DataSnap or SOAP server on a Linux web server – but that’s a story for another day.
Installation and first use
Let’s see how easy (or hard) it is to install and use RemObjects SDK.
A trial version is available for download from their website at http://www.RemObjects.com, so you are free to try for yourself.
Installation is quite straightforward (you only have to take care not to use any special versions of Indy, otherwise you need to rebuild a package).
After you’ve installed RemObjects SDK, there will be a new tab in the Object Repository with no less than seven icons.
Six to start a new RemObjects Server Project, and one to create a specific RemObjects DataSnap Server Module.
The latter can be used to combine RemObjects SDK with DataSnap and will also be covered at another time.
The other six can be used to create different kinds of RemObjects server targets: from Apache, Apache 2, CGI, ISAPI/NSAPI (all based on a WebBroker web module) to a regular Windows or DLL project.
The Windows executable can also be turned into an NT service with a simple checkbox option (really useful, since it means that nobody has to be logged in on the server machine to run the RemObjects server application itself).
Apart from those targets, it’s good to know that IntraWeb and ExpressWeb Framework (web server) applications can also host RemObjects servers.
Conversion Server
As a quick demo, I want to use RemObjects SDK to build a CGI Server Project (one that I can deploy on my eBob42 website and is actually deployed by the time you read this article).
If you want to play along, just double-click on the CGI Server Project icon, which will start the RemObjects CGI Server Project dialog.
In this dialog, I can specify the project name (ConversionServer), the service library name (Conversion) and the Service Name (MetricConversions).
Note that by default one Service will be added to my project, but I can always add more services later.
program ConversionServer; {$APPTYPE CONSOLE} {#ROGEN:Conversion.rodl} // RemObjects: Careful, do not remove! uses WebBroker, CGIApp, Unit1 in 'Unit1.pas' {WebModule1: TWebModule}; {$R *.RES} {$R RODLFile.RES} // RemObjects: Careful, do not remove! begin Application.Initialize; Application.CreateForm(TWebModule1, WebModule1); Application.Run; end.Note the two lines with the RemObjects SDK comments to warn you not to remove them. The RODL file contains the Remote Objects Definition Language that defines the services for our library. This is a human-readable XML file (much easier to read than a WSDL file for a standard SOAP service). Apart from the external RODL file itself, the RODLFile.res seems to contain the Windows binary resource version of the RODL file contents (so it’s linked in with your application). RemObjects SDK comes with a special ServiceBuilder utility (installed in the Delphi menu between the Tools and Windows menus) that can help you to work on the contents of the RODL file, and generate the corresponding Delphi source code as well as keep the RODLFile.res up to date.
ServiceBuilder
The ServiceBuilder can be started from the RemObjects menu in the Delphi IDE.
It can be compared to a Type Library, in that we can use it to define new interfaces, methods, but also types and more.
If you first start the ServiceBuilder dialog, it shows that the MetricConversions service already contains two sample methods: Sum and GetServerTime.
Since you probably don’t want those, click on the button with the minus character right on top of the two methods to delete them.
Now click on the plus button to add a new method.
In fact, let’s add a few methods to convert meters to other (non-standard) distance units.
The actual definition and implementation of these metric conversion routines can be found in the Delphi 7 unit StdConvs.pas, from which I want to use the distance unit values for Meters, Inches, Feet and Yards and Miles, and while we’re at it, also for Light Years.
As a true European and user of the ISO standard, I want all my conversions to be either from or to the Meter, which yields the following 10 methods:
ConvertMetersToInches ConvertInchesToMeters ConvertMetersToFeet ConvertFeetToMeters ConvertMetersToYards ConvertYardsToMeters ConvertMetersToMiles ConvertMilesToMeters ConvertMetersToLightYears ConvertLightYearsToMetersEach of the above is a function taking a double argument (the input value), returning a double again – the converter value. Using the ServiceBuilder we can quickly define these 10 functions.
unit MetricConversions_Impl; {----------------------------------------------------------------------------} { This unit was automatically generated by the RemObjects SDK after reading } { the RODL file associated with this project. } { } { This is where you are supposed to code the implementation of your objects. } {----------------------------------------------------------------------------} interface uses {vcl:} Classes, {RemObjects:} uROClientIntf, uROServer, uROServerIntf, {Generated:} Conversion_Intf; type TMetricConversions = class(TRORemotable, MetricConversions) private protected function ConvertMetersToInches(const Value: Double): Double; function ConvertMetersToFeet(const Value: Double): Double; function ConvertMetersToYards(const Value: Double): Double; function ConvertMetersToMiles(const Value: Double): Double; function ConvertMetersToLightYears(const Value: Double): Double; function ConvertInchesToMeters(const Value: Double): Double; function ConvertFeetToMeters(const Value: Double): Double; function ConvertYardsToMeters(const Value: Double): Double; function ConvertMilesToMeters(const Value: Double): Double; function ConvertLightYearsToMeters(const Value: Double): Double; end; implementation uses {Generated:} Conversion_Invk, StdConvs; { StdConvs defines the following constants for us: MetersPerInch = 0.0254; MetersPerFoot = MetersPerInch * 12; MetersPerYard = MetersPerFoot * 3; MetersPerMile = MetersPerFoot * 5280; MetersPerLightSecond = 2.99792458E8; MetersPerLightYear = MetersPerLightSecond * 31556925.9747; } procedure Create_MetricConversions(out anInstance : IUnknown); begin anInstance := TMetricConversions.Create; end; function TMetricConversions.ConvertMetersToInches(const Value: Double): Double; begin Result := Value / MetersPerInch end; function TMetricConversions.ConvertMetersToFeet(const Value: Double): Double; begin Result := Value / MetersPerFoot end; function TMetricConversions.ConvertMetersToYards(const Value: Double): Double; begin Result := Value / MetersPerYard end; function TMetricConversions.ConvertMetersToMiles(const Value: Double): Double; begin Result := Value / MetersPerMile end; function TMetricConversions.ConvertMetersToLightYears(const Value: Double): Double; begin Result := Value / MetersPerLightYear end; function TMetricConversions.ConvertInchesToMeters(const Value: Double): Double; begin Result := Value * MetersPerInch end; function TMetricConversions.ConvertFeetToMeters(const Value: Double): Double; begin Result := Value * MetersPerFoot end; function TMetricConversions.ConvertYardsToMeters(const Value: Double): Double; begin Result := Value * MetersPerYard end; function TMetricConversions.ConvertMilesToMeters(const Value: Double): Double; begin Result := Value * MetersPerMile end; function TMetricConversions.ConvertLightYearsToMeters(const Value: Double): Double; begin Result := Value * MetersPerLightYear end; initialization TROClassFactory.Create('MetricConversions', Create_MetricConversions, TMetricConversions_Invoker); finalization end.After having written 10 lines of code in the skeleton that ServiceBuilder already created for us, it’s almost time to build the RemObjects server.
SmartService
Before we can build and deploy the project, we have to go to the Web Module (in Unit1.pas) and specify a value for the CustomLocation property of the ROMessage component.
The value should be http://www.eBob42.com/cgi-bin/ConversionServer.exe/SOAP since I want to deploy it on my own website.
Setting this option may not be necessary for an ISAPI (or stand-alone RemObjects) server, but apparently there’s a small bug in Delphi’s CGI framework that doesn’t pass the Request.Host and Request.URL values correctly.
It doesn’t hurt, just something to think about.
The Web Module that was generated for us has two components: apart from the ROMessage component (of type TROSOAPMessage), there is also the ROServer (of type TROWebBrokerServer).
Using the TROSOAPMessage will make sure that my RemObjects CGI application uses SOAP as message format.
However, if I also want to use the more efficient binary format, I need to drop a TROBINMessage component from the RemObjects tab of the Component Palette onto the Web Module.
RemObjects Client
Apart from importing the WSDL definition of our RemObjects server with Delphi’s WSDL Importer, I also want to show you how we can connect to a RemObjects server using the SOAP and BIN protocols and RemObjects’ own components.
Like I said, any application can be a RemObjects client, so start a regular new Delphi application and drop five components from the RemObjects tab: a TROSOAPMessage, TROBINMessage, two TROBPDXHTTPChannels and one TRoPoweredByRemObjectsButton.
The last one is only used to show the “Powered by RemObjects” button, of course.
The two message buttons are used to communicate with their counterpart in the RemObjects server application.
Since both need to connect to a different URL (one ending with /SOAP and the other ending with /BIN), we need two different TROBPDXHTTPChannel components that use HTTP to connect to a given TargetURL - http://www.eBob42.com/cgi-bin/ConversionServer.exe/SOAP for SOAP and http://www.eBob42.com/cgi-bin/ConversionServer.exe/BIN for the binary protocol.
To finish the user interface, I’ve used a RadioGroup (with 10 options to allow us to select one of the ten possible conversions), two LabelEdits (to hold the input and output values), and three buttons, for three different connections.
unit ClientForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, uROBaseConnection, uROProxy, uROBPDXTCPChannel,
uROBPDXHTTPChannel, uROClient, uROSOAPMessage, uROBINMessage,
uROPoweredByRemObjectsButton, ExtCtrls;
type
TForm2 = class(TForm)
btnRemObjectsSOAP: TButton;
ROSOAPMessage1: TROSOAPMessage;
ROBPDXHTTPChannel1: TROBPDXHTTPChannel;
btnBorlandSOAP: TButton;
ROBINMessage1: TROBINMessage;
btnRemObjectsBIN: TButton;
RoPoweredByRemObjectsButton1: TRoPoweredByRemObjectsButton;
ROBPDXHTTPChannel2: TROBPDXHTTPChannel;
Conversion: TRadioGroup;
ledInput: TLabeledEdit;
ledOutput: TLabeledEdit;
procedure btnRemObjectsSOAPClick(Sender: TObject);
procedure btnBorlandSOAPClick(Sender: TObject);
procedure btnRemObjectsBINClick(Sender: TObject);
end;
var
Form2: TForm2;
implementation
uses
ImportedService, uMetricConversions;
{$R *.dfm}
procedure TForm2.btnBorlandSOAPClick(Sender: TObject);
var
Value: Double;
begin
with GetMetricConversions do
begin
Screen.Cursor := crHourGlass;
try
Value := StrToFloatDef(ledInput.Text,0);
case Conversion.ItemIndex of
0: Value := ConvertMetersToInches(Value);
1: Value := ConvertMetersToFeet(Value);
2: Value := ConvertMetersToYards(Value);
3: Value := ConvertMetersToMiles(Value);
4: Value := ConvertMetersToLightYears(Value);
5: Value := ConvertInchesToMeters(Value);
6: Value := ConvertFeetToMeters(Value);
7: Value := ConvertYardsToMeters(Value);
8: Value := ConvertMilesToMeters(Value);
9: Value := ConvertLightYearsToMeters(Value)
end;
ledOutput.Text := FloatToStr(Value)
finally
Screen.Cursor := crDefault
end
end
end;
procedure TForm2.btnRemObjectsSOAPClick(Sender: TObject);
var
Value: Double;
begin
with CoMetricConversions.Create(ROSOAPMessage1, ROBPDXHTTPChannel1) do
begin
Screen.Cursor := crHourGlass;
try
Value := StrToFloatDef(ledInput.Text,0);
case Conversion.ItemIndex of
0: Value := ConvertMetersToInches(Value);
1: Value := ConvertMetersToFeet(Value);
2: Value := ConvertMetersToYards(Value);
3: Value := ConvertMetersToMiles(Value);
4: Value := ConvertMetersToLightYears(Value);
5: Value := ConvertInchesToMeters(Value);
6: Value := ConvertFeetToMeters(Value);
7: Value := ConvertYardsToMeters(Value);
8: Value := ConvertMilesToMeters(Value);
9: Value := ConvertLightYearsToMeters(Value)
end;
ledOutput.Text := FloatToStr(Value)
finally
Screen.Cursor := crDefault
end
end
end;
procedure TForm2.btnRemObjectsBINClick(Sender: TObject);
var
Value: Double;
begin
with CoMetricConversions.Create(ROBINMessage1, ROBPDXHTTPChannel2) do
begin
Screen.Cursor := crHourGlass;
try
Value := StrToFloatDef(ledInput.Text,0);
case Conversion.ItemIndex of
0: Value := ConvertMetersToInches(Value);
1: Value := ConvertMetersToFeet(Value);
2: Value := ConvertMetersToYards(Value);
3: Value := ConvertMetersToMiles(Value);
4: Value := ConvertMetersToLightYears(Value);
5: Value := ConvertInchesToMeters(Value);
6: Value := ConvertFeetToMeters(Value);
7: Value := ConvertYardsToMeters(Value);
8: Value := ConvertMilesToMeters(Value);
9: Value := ConvertLightYearsToMeters(Value)
end;
ledOutput.Text := FloatToStr(Value)
finally
Screen.Cursor := crDefault
end
end
end;
end.
The final screenshot shows the client in action, connecting to the RemObjects server as deployed on the eBob42.com website, and converting 42 meters to 1653.5 inches.
Support
RemObjects SDK is quite new, and to be honest, the documentation on the website isn’t always finished (there are a number of articles and tutorials that can use some more flesh).
By the time of reading, this “issue” may well have been solved already, and this article tries to help you a little bit as well.
The product does include a number of sample applications that provide insight to the inner workings, and guidelines how to proceed by yourself.
However, if you ever encounter problems, it’s good to know that the support in the newsgroups is quick and good.
Conclusion
In short, I’m impressed by what RemObjects SDK can do, especially when it comes to flexibility (adding your own message formats or communication channel protocols) and efficiency.
I can’t wait until the Kylix version comes out to I can try this in a cross-platform setting (and a .NET version will also be welcome, of course).
If you’re only remotely interesting in this stuff, make sure to download the trial version and find out what it can mean for you.
Feel free to share your experiences, good or bad.
You know where to find me!
A RemObjects SDK full license can be purchased from RemObjects Software for US$250 (per developer).
It comes with full source code for Delphi 5, 6 and 7.
There are no runtime fees that you have to pay.
A trial version is also available, so check the RemObjects Software website.
(Bob Swart)