2004. április 19., hétfő

Exploring web services in Delphi


Problem/Question/Abstract:

How to write a Web Services Server in Delphi?

Answer:

This article is a continuation of my previous article "Accessing web services using SOAP". In that article, we have seen how can we write a Web Services Client in Delphi using a WSDL file and now we are going to see how can we write a Web Services Server itself in Delphi 6.

Why Web Services?

Web services are basically designed to allow loose coupling between client and server and also they dont require clients to use a specific platform or language. i.e. it's language neutral. Mainly for these reasons among others, these services are becoming more popular and no doubt, they are going to dominate the industry in future.

How Delphi is implementing these services?

Web services server developed in Delphi are using Invokable interfaces. These are interfaces that contain Run Time Type Information(RTTI) and this information will help interpreting calls from clients. There is a separate class defined in Delphi called IInvokable and whenever we write a web services server in Delphi, it should  use this class. This class is basically derived from IInterface class.

Whenever a client uses a service by calling one of its methods, that method has to be identified and a proper response has to be given to the client. Right? For that purpose, there are two more components in addition to Invokable interfaces.

1. Dispatcher

Whenever a client requests a service by calling a specific method in the server, that request has been passed on to the Invoker. No need to say, the request will always be a SOAP message.  This dispatcher is implemented in Delphi using a separate class called THTTPSoapDispather.

2. Invoker

Once it receives the SOAP message from the Dispatcher, it finds the relevant interface and executes the call and send a response as another SOAP message. This Invoker is implemented using another class called THTTPSoapPascalInvoker.

And one more thing, as of now these two classes are designed to support only HTTP request and response.

In total, all we need is nothing but interfaces and classes that implement those interfaces. That's all. Once you desinged both, you need to register them. After that, all the request/response will be handled by both the Dispatcher and Invoker. Pretty simple?   huh?

Now let us try writing a simple Web Services Server in Delphi 6. I'm not going to give fully functionaly example of a web service server rather i'm just going to discuss what are all the steps in developing a Web services server in Delphi with some sample units.

The following is a sample Interface unit :

unit uConversionIntf;

interface

type
  ITConversion = interface(IInvokable)
    ['{878DD241-526E-48CD-90B4-2749471D2DE5}']
    //A sample function to convert Celcius to Farenheit
    function CelciusToFahrenheit(Celcius: Real): Real; stdcall;
    {Here you can define your services}
  end;

implementation

uses
  InvokeRegistry; // Unit contains methods on how to register the interface

initialization
  InvRegistry.RegisterInterface(TypeInfo(ITConversion));

end.

The unit above is a sample having some sample methods that define the web services. Here I added a function to convert Celcius to Farenheit. Like that you can define as many functions as possible. And the "['{878DD241-526E-48CD-90B4-2749471D2DE5}']" is nothing but the GUID for that interface ITConversion. This you can generate in the Delphi IDE by pressing Ctrl+Shift+G, the easiest way of creating GUID in Delphi.

The unit InvokeRegistry contains methods needed to register the interface with your system. We need to register that interface in the initialization section. That's all about defining the interface.

Then the next step is to implement the interface in a class. For that we need to create another unit with the implementation class.

Here is the sample implementation class:

unit ConversionImpl;

interface

uses
  InvokeRegistry;

type
  TConversionService = class(TInvokableClass, ITConversion)
  public
    function CelciusToFahrenheit(Celcius: Real): Real; stdcall;
  end;

implementation

function CelciusToFahrenheit(Celcius: Real): Real; stdcall;
begin
  //Here you can write the code to convert the celcius to farenheit.
end;

initialization
  InvRegistry.RegisterInvokableClass(TConversionService);
end.

Here also you can see in the initialization section that you need to register the implementation class with the invokation registry. Now we have two units, one defining the interface and the other implementing that interface in a class.

As of now, we need to create these two units by hand and add code to register both the interfaces and implementation classes; It would be nice if we have a wizard of some kind to create these two units automatically. I dont see any such wizard right now in Delphi 6 Trial Edition downloaded version. And I heard that there is a wizard available in Delphi 6 to generate these two files. If not, I'm thinking of writing such a wizard. Please keep me informed if you know anything further on this.

Next step is to create a SOAP application in Delphi and include these two units into that application. Once you are done that, the Web services server in Delphi in ready to use.

How to create a SOAP server application?

In the Delphi IDE, select New | Other | Web Services and select SOAP Server Application.
Then there will be a dialog showing you the types of web servers.
You can choose the type you want; for our example purpose, letz choose ISAPI/NSAPI Dynamic Link Library
This will create a web module with three components, out of that we discussed two of 'em already and the new component is the TWSDLHTMLPublish. (Let us see about this later in this article)

The sample source of the web module would look like this:

unit uConversion;

interface

uses
  SysUtils, Classes, HTTPApp;

type
  TWebModule1 = class(TWebModule)
    HTTPSoapDispatcher1: THTTPSoapDispatcher;
    HTTPSoapPascalInvoker1: THTTPSoapPascalInvoker;
    WSDLHTMLPublish1: TWSDLHTMLPublish;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  WebModule1: TWebModule1;

implementation

{$R *.DFM}

end.

In this unit, you can see both the dispatcher and invoker in addition to the WSDL publisher component.

Now add both the Interface and the implementation units we created before to this newly created project.
Once you added those two unit, build the application and this will result in a DLL;

How can we use this server in Delphi?

This is pretty simple. You just need to add the Interface unit into your normal Delphi application and call the functions implemented in the server.

How can we use this server in applications other than Delphi?

Here comes the WSDL file. You just need to generate the WSDL with the server you have written. How can we do that? Now comes the WSDLHTMLPublish component. This component will help you generate that WSDL file.

What you need to generate/publish that WSDL file?

You need a web server, atleast a Personal Web Server and should be running.
You just need to put that DLL in the C:\Inetpub\Scripts directory and browse that DLL through a web server. i.e. http://localhost/scripts/

So when you browse through a web server, you will be getting the WSDL file. Also if you set the AdminEnabled property of the WSDLHTMLPublish component to true, you would be able to see the administrator previleges with a click of an additional button on the browser.

This is a simple type of web server without any much complexities. The example I have explained involved only simple data types like Real, String etc., but actually you can write a server that return complex types like returning an object etc., But this would be the basic that you need to understand before writing such complex ones.

P.S.: One of our members reported me of a problem viewing the WSDL file while browsing the dll through a web server. Actually I forgot to mention one thing about that. We need to specify WSDL after the dll name while browing through a web server. i.e. http://localhost/scripts/ConversionService.dll/WSDL.

And the other problem is unloading the DLL from memory after first use. The solution for this is to stop the web server and try building the dll again. It should normally work. Because when you use for the first time through a web server, that web server will hold a reference to that dll; so if you stop the web server that reference should be freed. This is been a problem with PWS and IIS; sometimes works sometimes not!! In that case, we may need to restart the entire machine itself(which is not a solution for this) ; I have been experiencing such problems often; so if anyone has any experience on this, please keep our members posted.

Nincsenek megjegyzések:

Megjegyzés küldése