Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics Dr.Bob's Delphi Courseware Manuals
 Dr.Bob Examines... #26
See Also: other Dr.Bob Examines columns or Delphi articles

An earlier version of this article originally appeared in Delphi Developer (December 2001). Copyright Pinnacle Publishing, Inc. All rights reserved.

Kylix 2 Enterprise Web Services
Kylix 2 has been shipping a few days as I write this article. Yet, it wasn't hard to start using the new features, since they are so much alike the ones we've been using in Delphi 6 in the past months. In this article, I want to move from XML coverage in Delphi 6 (what I've done in the past months) to SOAP support - in both Delphi 6 and Kylix 2. This time, I want to start with the steps to build a Web Service in Kylix 2 using SOAP. Next time, I continue the story and turn it into a cross-platform architecture by writing both a Kylix 2 and a Delphi 6 Client for my Kylix 2 Web Service. This and more next time, but first let's build a Kylix 2 Web Service.

Web Services
The topic of web services is hot. In fact, there's hardly a place where it isn't mentioned these days. Personally, I see web services are a natural evolution of more "regular" web server applications. A web service can be seen as a web server application without the user interface - an engine. Just like we've been able to split stand-alone applications into clients and servers, and latter adding middle-ware, we can now distribute the server-side engines as "web services", using HTTP, FTP or even e-mail as communication protocol.
If we look inside a web service, we see a SOAP object (some functional "interface" accessible through the Simple Object Access Protocol), which functional description - the available methods and their arguments - is made available using WSDL - the Web Service Description Language (a language based on XML).

Kylix 2 Enterprise
Note that you'll need Kylix 2 Enterprise edition to be able to build Web Services. If you want to try it without buying Kylix 2 Enterprise, yet, then you can always download the 60-day trial edition from the Borland website. It'll still work when we get back here next month, so why wait?

K2 SOAP Server Application
To start a Soap Server application in Kylix 2, you have to start a new application using File | New, then go to the WebServices tab of the Object Repository. There, you'll see three icons with descriptions that may not be complete (at least, I can't see the complete text in my Red Hat 6.0 installation that I still use):

Just to explain, the first icon stands for the "SOAP Server Application", the second is the "SOAP Server Data Module" and the third is the "Web Service Importer". Today, we'll only use the first icon, next time we'll also use the third icon (and the second icon is described on my website at http://www.drbob42.com/soap42.htm already).
For our Web Service application, we need to select the SOAP Server Application icon (the first one) and click on OK. This will give the "New Soap Server Application" wizard, in which we can only select the type of web server application. This enforces my claim that a web service is just a special web server application. On Linux, we are able to create a CGI stand-alone executable or a special Apache shared module (DSO). Since the latter requires special configuration (and recompilation) of you Apache web server, I will not select the default choice of a CGI application (see the Kylix Developer's Guide for more information about configuring Apache DSO).

Once you click on OK, you get a new Web Module, but with three "web service" components already placed: a HTTPSoapDispatcher (to dispatch the incoming SOAP requests), a HTTPSoapPascalInvoker (to call the correct Pascal method based on a Soap request) and finally a WSDLHTMLPublish component (which will generate the WSDL to describe the functionality of our web service to the outside world).

We don't have to do anything with these components: they are ready to be used. We only have to focus on the task at hand: build some kind of "engine", and define it's "interface" to it can be published and used. As engine example, I've decided to use something with a little bit of relevance for some European countries: the Euro conversion (as of 1/1/2002 a number of European countries are converting to the Euro as currency). This kind of "non-visual conversion" is a typical and well-suited example to implement using a web service.
Before we continue, save the web module in file SWebMod.pas and the project itself in file Euro42.dpr (this will result in a CGI web server (web service) application called Euro42).

Euro Conversion
There are 12 countries that participate in the Euro. Which also mean 12 different conversion rates. For each country, we need the 3 letter acronym for the national currency as well as the conversion rate to Euros. Then, we can make 24 functions that can convert to and from each individual currency. These 24 methods will define the interface of our "Euro" web service. However, since other countries could participate in the future as well, it may be more practical to have just two general "Conversion" methods: FromEuro and ToEuro, each taking a shortstring (the name of the currency) and currency value as argument, returning a converted currency value. This way, it would be very easy to add new countries (just make sure we can understand the new currency strings, instead of adding new methods and thereby changing the interface).

Euro Interface
Let's start with the interface first. If we want to export the interface using a web service - in other words make it available as an "invokable interface" from the outside world, then we have to derive it from IInvokable. We also need to register our interface in the InterfaceRegistry, as implemented in unit Euro.pas, which can be seen in Listing 1:

  unit Euro;
  interface
  uses
    InvokeRegistry;

  type
    IEuro = interface(IInvokable)
      function FromEuro(const Currency: WideString; Amount: Double): Double; stdcall;
      function ToEuro(const Currency: WideString; Amount: Double): Double; stdcall;
    end;

  implementation

  initialization
    InvRegistry.RegisterInterface(TypeInfo(IEuro));
  end.
Note that the two methods of our IEuro interface are declared using the stdcall calling convention. This is required, so other environments can actually call these methods.
Also note that the definition of IEuro from Listing 1 does not contain a GUID, which would have been required if we had defined this IEuro interface in Delphi 6 Enterprise. Next time, we'll see the GUID resurface in Delphi 6, when we're creating a web service client for the IEuro.

Euro Implementation
The list of currency acronyms and conversion rates (1 EURO = X) can be obtained from a number of sources - and please don't blame me if I mistyped a digit here. I have defined a type TEuroCurrency that consist of two fields: a Currency string (only three characters are used) and a Conversion field of type Double).
Now that we have the conversion rates, it's time to write the two conversion functions FromEuro and ToEuro, which are part of the TEuro class. TEuro must be derived from TInvokableClass, implementing the IEuro interface. Both methods from the IEuro interface have two arguments: a literal string containing the currency as well as a double containing the current amount (either in the local currency when calling ToEuro, or in Euros when calling the FromEuro function). They return a Double value again.
Using a pre-initialised list (array) of Currencies ensures that the of "EuroCountries" can easily be extended in a future version of this web service. The complete implementation of TEuro is available in unit EuroImpl.pas, which can be seen in Listing 2:

  unit EuroImpl;
  interface
  uses
    Euro, InvokeRegistry;

  const
    EuroCountries = 12;

  type
    TEuroCurrency = record
      Currency: String[3];
      Conversion: Double;
    end;

  const
    Currencies: Array[1..EuroCountries] of TEuroCurrency = (
       (Currency: 'FIM'; Conversion: 5.94573),
       (Currency: 'ITL'; Conversion: 1936.27),
       (Currency: 'NLG'; Conversion: 2.20371),
       (Currency: 'ESP'; Conversion: 166.386),
       (Currency: 'BEF'; Conversion: 40.3399),
       (Currency: 'ATS'; Conversion: 13.7603),
       (Currency: 'LUF'; Conversion: 40.3399),
       (Currency: 'DEM'; Conversion: 1.95583),
       (Currency: 'FRF'; Conversion: 6.55957),
       (Currency: 'IEP'; Conversion: 0.787564),
       (Currency: 'PTE'; Conversion: 200.482),
       (Currency: 'GRO'; Conversion: 340.750));

  type
    TEuro = class(TInvokableClass, IEuro)
      function FromEuro(const Currency: WideString; Amount: Double): Double; stdcall;
      function ToEuro(const Currency: WideString; Amount: Double): Double; stdcall;
    end;

  implementation

  { TEuro }

  function TEuro.FromEuro(const Currency: WideString; Amount: Double): Double;
  var
    curr:  Integer;
  begin
    curr := 1;
    while (curr <= EuroCountries) and
     (Currencies[curr].Currency <> Currency) do Inc(curr);
    if curr <= EuroCountries then
      Result := Amount * Currencies[curr].Conversion
    else Result := 0 { invalid country code }
  end;

  function TEuro.ToEuro(const Currency: WideString; Amount: Double): Double;
  var
    curr:  Integer;
  begin
    curr := 1;
    while (curr <= EuroCountries) and
     (Currencies[curr].Currency <> Currency) do Inc(curr);
    if curr <= EuroCountries then
      Result := Amount / Currencies[curr].Conversion
    else Result := 0 { invalid country code }
  end;

  initialization
    InvRegistry.RegisterInvokableClass(TEuro);
  end.
Note that apart from registering the IEuro interface (as is done in unit Euro.pas), we also need to register the implementation class TEuro itself.

Euro Deployment
Now that we've defined the IEuro interface and implemented the TEuro class, it's time to deploy the Euro42 web service. Since we wrote it as CGI stand-alone executable, we only have to copy it to the httpd/cgi-bin directory of a Linux machine. There are no further deployment needs (apart from the fact that legally you are not allowed to deploy applications made with the 60-day trial edition of Kylix 2, in case you don't use the "full" Kylix 2 Enterprise version for your web service).
In order to test the web server, you can start a browser and type the full URL to the web server application, appending it with the /wsdl string in order to request the Web Service Description Language index. This will give an overview of all SOAP interfaces (and implementations) available within the web service.

As you can see, there is a WSDL for IWSDLPublish (the actual publisher of our interfaces) and there is a WSDL for IEuro - the web service we're interested in. The former will be available in all web services that you build with Kylix 2 (or Delphi 6) in this way.
When you click on the WSDL for IEuro link, you in fact request the WSDL definition for the IEuro interface, which is specified in XML. Since the Netscape browser on Linux cannot display XML (not without a proper plug-in), you are prompted with a Save As... dialog for the type text/xml. Feel free to save this file, because we can use it next time when we're going to write Web Service clients (also called Web Service Consumers) for this particular "Euro" Web Service in Delphi 6, Kylix 2 and even JBuilder.


This webpage © 2002-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.