Importing and Consuming external Web Services

Being able to import and use services provided by other applications, be they home or third party built, is an essential aspect of Web application development. It is also one of the most fundamental tenets of a service oriented architecture. While other topics in this documentation focus on the creation of Web Services, this topic will focus exclusively on how they can be used from a Morfik application.


Importing an External Web Service

Morfik makes using Web Services which are external to your current application, very easy. The only thing you need to start, is to have the WSDL file of the service you wish to use. For the sake of simplicity, I have chosen to import the WSDL file which is generated by a very simple math sample, in this topic. In Figure 1 you can see the Web Services option, in the Home tab of the riboon, when in the Project View of the Morfik development environment.


web-methods-web-services-button.png
Figure 1 Import Web Services menu option.


This wizard makes it extremely easy for you to import any web service you want (provided you have access to its WSDL definition file), into your project.

Once the wizard is started, it will ask for the WSDL file that describes the service to be imported, as can be seen in Figure 2.


web-methods-wsdl-import.png
Figure 2 Dialog for selecting the WSDL file to be imported.


Once you have selected the WSDL file the wizard will display all the methods found in the service description, with their respective parameters, as shown in Figure 3.


web-methods-import-math-service.png
Figure 3 Methods (with parameters) available from the selected service.


Next you will be asked if you will want to invoke the imported methods directly from the browser. If the answer to that is yes and the service you are importing will not be running under the same domain name as the application you are coding, you should choose the option that enables the usage of a server side proxy. This proxy is handled automatically by the Morfik Framework and will allow you to code your application as if you were accessing these 3rd party services as if they were running on your servers, despite the browser security restrictions.


web-methods-browser-code-generation.png
Figure 4 Browser code generation and its options.


After you have completed the Import Web Service wizard, the IDE will have generated a dual module with the classes required to correctly call out to the imported methods in as easy a manner as possible from both the browser and server sides of your application. You can see the code for the server side portion of this module for these methods in Listing 1, and the browser side portion in Listing 2.

Listing 1 – Code for the automatically generated interface to the imported services

FX Code

Unit MathTestService;
 
Interface
 
Uses
    SystemClasses,
    SystemSerialiser,
    SystemXml;
 
Type
TMathTestServiceSoapIntf = Class(TSoapHttpClient)
    Procedure   MathProcessor (operand1 : double; operand2 : double; theOperator : string; 
                           Var theResult : double; Var Status_1 : longint); Virtual;
    Procedure   MathProcessor2(Operand1 : double; Operand2 : double; theOperator : string; 
                           Var theResult : double; Var Status_1 : longint); Virtual;
End;
 
Implementation
 
{.................... MathTestServiceSoapIntf ....................}
 
Procedure TMathTestServiceSoapIntf.MathProcessor(operand1 : double; operand2 : double; 
    theOperator : string; Var theResult : double; 
    Var Status_1 : longint);
Begin
    BeginInvoke('http://localhost:9119/?cls=soap',
        'MathProcessor xmlns="http://tempuri.org/"',
        'MathProcessorResponse xmlns="http://tempuri.org/"',
        'MathProcessor',ssDocumentWrapped);
    Try
        Try
            RequestBody.WriteDouble('operand1 xsi:type="xsd:double"', operand1);
            RequestBody.WriteDouble('operand2 xsi:type="xsd:double"', operand2);
            RequestBody.Writestring('theOperator xsi:type="xsd:string"', theOperator);
 
            Invoke();
 
            ReplyBody.ReadDouble('theResult xsi:type="xsd:double"', theResult);
            ReplyBody.Readinteger('Status xsi:type="xsd:int"', Status_1);
        Except
            Raise;
        End
    Finally
        EndInvoke();
    End
End;
 
 
Procedure TMathTestServiceSoapIntf.MathProcessor2(Operand1 : double; Operand2 : double; 
    theOperator : string; Var theResult : double; 
    Var Status_1 : longint);
Begin
    BeginInvoke('http://localhost:9119/?cls=soap',
        'MathProcessor2 xmlns="http://tempuri.org/"',
        'MathProcessor2Response xmlns="http://tempuri.org/"',
        'MathProcessor2',ssDocumentWrapped);
    Try
        Try
            RequestBody.WriteDouble('Operand1 xsi:type="xsd:double"', Operand1);
            RequestBody.WriteDouble('Operand2 xsi:type="xsd:double"', Operand2);
            RequestBody.Writestring('theOperator xsi:type="xsd:string"', theOperator);
 
            Invoke();
 
            ReplyBody.ReadDouble('theResult xsi:type="xsd:double"', theResult);
            ReplyBody.Readinteger('Status xsi:type="xsd:int"', Status_1);
        Except
            Raise;
        End
    Finally
        EndInvoke();
    End
End;
 
Begin
End.



This unit (Listing 1) has very little auxiliary and support code being primarily a wrapper around the necessary information serialization code. From that listing what should mainly interest the programmer, who is a user (consumer) of the web service, is a class that offers two methods, each tied to a "function" (a web method, by its own right) of the service. You can see this classes interface below:


FX Code

TMathTestServiceSoapIntf = Class(TSoapHttpClient)
    Procedure   MathProcessor (operand1 : double; operand2 : double; 
                           theOperator : string; Var theResult : double; 
                           Var Status_1 : longint); Virtual;
    Procedure   MathProcessor2(Operand1 : double; Operand2 : double; 
                           theOperator : string; Var theResult : double; 
                           Var Status_1 : longint); Virtual;
End;


Careful observation of this code will undoubtedly reveal that this class’ methods are, in fact, an exact representation of what WebMethods we, originally, added to our application


Listing 2 – Code for the automatically generated browser side interface to the imported services

FX Code

Unit MathTestService;
['AutoSynchronize=None'];
 
Interface
 
Uses
    SystemUtilities,
    SystemClasses,
    SystemRPC,
    SystemCatalog,
    SystemWebMethod;
 
Type
TMathTestServiceMethod = Class(TSoapClient)
    Constructor Create(aMethodName : String);
End;
 
TMathProcessor = Class(TMathTestServiceMethod)
    operand1    : double;
    operand2    : double;
    theOperator : string;
    theResult   : double;
    Status_1    : longint;
    Constructor Create;
    procedure Execute; ['AutoSynchronize=Manual']; Override;
End;
 
TMathProcessor2 = Class(TMathTestServiceMethod)
    Operand1    : double;
    Operand2    : double;
    theOperator : string;
    theResult   : double;
    Status_1    : longint;
    Constructor Create;
    procedure Execute; ['AutoSynchronize=Manual']; Override;
End;
 
Implementation
 
{.................... MathTestServiceSoapIntf ....................}
 
Constructor TMathTestServiceMethod.Create(aMethodName : String);
Begin
    Inherited Create(aMethodName);
    UseProxyServer := True;
    ServiceURL     := 'http://localhost:9119/?cls=soap';
End;
 
Constructor TMathProcessor.Create;
Begin
    Inherited Create('MathProcessor');
    SoapAction := 'MathProcessor';
End;
 
Procedure TMathProcessor.Execute;
Begin
    With Self Do
    Begin
        Self.Bind('operand1',@operand1);
        Self.Bind('operand2',@operand2);
        Self.Bind('theOperator',@theOperator);
        Self.Bind('theResult',@theResult);
        Self.Bind('Status',@Status_1);
    End;
    Inherited Execute;
End;
 
 
Constructor TMathProcessor2.Create;
Begin
    Inherited Create('MathProcessor2');
    SoapAction := 'MathProcessor2';
End;
 
Procedure TMathProcessor2.Execute;
Begin
    With Self Do
    Begin
        Self.Bind('Operand1',@Operand1);
        Self.Bind('Operand2',@Operand2);
        Self.Bind('theOperator',@theOperator);
        Self.Bind('theResult',@theResult);
        Self.Bind('Status',@Status_1);
    End;
    Inherited Execute;
End;
 
 
Function MathTestService_CreateList(Name : String) : Pointer;
Begin
    Result := Nil;
End;
 
Procedure MathTestServiceSRLZRegister;
Begin
    With TSerializableMethodInfo.Create('MathProcessor') Do
    Begin
        RequestNameSpace := 'http://tempuri.org/';
        RequestNameSpaceAlias := '';
        AddField('operand1','double',ptIn);
        AddField('operand2','double',ptIn);
        AddField('theOperator','string',ptIn);
        AddField('theResult','double',ptOut);
        AddField('Status','longint',ptOut);
    End;
 
    With TSerializableMethodInfo.Create('MathProcessor2') Do
    Begin
        RequestNameSpace := 'http://tempuri.org/';
        RequestNameSpaceAlias := '';
        AddField('Operand1','double',ptIn);
        AddField('Operand2','double',ptIn);
        AddField('theOperator','string',ptIn);
        AddField('theResult','double',ptOut);
        AddField('Status','longint',ptOut);
    End;
 
End;
 
Begin
    MathTestServiceSRLZRegister;
End.


This module (Listing 2) has a good quantity of auxiliary and support code which is not of much interest to the user of services. From that listing what should mainly interest the programmer, who is a user (consumer) of the web service, are two classes that embody the services definition. Each class is tied to a "functionailty" (a web method, by its own right) of the service. You can see the interface for these classes below:

FX Code

TMathProcessor = Class(TMathTestServiceMethod)
    operand1    : double;
    operand2    : double;
    theOperator : string;
    theResult   : double;
    Status_1    : longint;
    Constructor Create;
    procedure Execute; ['AutoSynchronize=Manual']; Override;
End;
 
TMathProcessor2 = Class(TMathTestServiceMethod)
    Operand1    : double;
    Operand2    : double;
    theOperator : string;
    theResult   : double;
    Status_1    : longint;
    Constructor Create;
    procedure Execute; ['AutoSynchronize=Manual']; Override;
End;

Calling a Imported Web Service

Once the Web Service has been imported into your project, calling it up is not difficult, though calling it from the Server or Browser portions of your application work differently.


Using a Web Service from the Server

If you create a version of our WebMethod test class, renaming the WebMethod as MathRequester, you can import the .WSDL file from the project where those web services are declared and change the Execute method of the MathRequester version of the Web Method to call the original Web Service in its first implementation. The main difference between this new application and the original one is that the MathRequester WebMethod does not process the operations; it forwards the request to the original application, now running as a service provider.

Only the name of the WebMethod was changed and its server side code. The code you see in Listing 3 is that of the Server side of the MathRequester Web Method.

Listing 3 – Server side code calling out to the imported Web Service.

FX Code

Unit MathRequester;
 
Interface
 
Type
MathRequester=Class(WebMethod)
    Operand1    : Double;
    Operand2    : Double;
    theOperator : String;
    theResult   : Double;
    Status      : Integer;
  Private
    { Private declarations }
  Public
    { Public declarations }
    Procedure Execute; override;
End; ['WSPublished=False'];
 
Implementation
 
Uses
  MathTestService;
 
Procedure MathRequester.Execute;
Var
  MathInt: TMathTestServiceSoapIntf;
Begin
   MathInt := TMathTestServiceSoapIntf.Create;
   Try
     MathInt.MathProcessor (Operand1, Operand2, theOperator, theResult, Status);
   Finally
     MathInt.Free;
   End;
End;
 
End.


You will notice that this service (MathRequester) has the same parameters as the remote target service (MathProcessor). This is necessary since the information is just going through this service, but it also makes it quite easy to handle the remote call and the return of the result (out) values to the browser side of your applications.

Using a Web Service from the Browser

As mentioned in the first part of this post, Morfik makes it possible for the developer to write client side code that makes direct references to a Web Service, even if it is published by a 3rd party. This is handled by the Morfik Framework that implements a transparent proxy on the server side portion of your application.

This leads to greater simplicity in writing applications that rely on such services as it is not necessary to create a new layer of web method code to transfer execution to the server and then relay the response. All this is handled transparently by the Morfik Framework without the need of any additional coding.

Listing 4 shows an example of what direct browser access to our MathProcessor sample Web Method would look like.

FX Code

Unit Content;
 
Interface
 
Uses SystemRPC;
 
Type
Content=Class(Form)
    TextLabel1       : TextLabel;
    TextLabel2       : TextLabel;
    btnExecuteMathOp : Button;
    Op1Edit          : TextEdit;
    Op2Edit          : TextEdit;
    Procedure btnExecuteMathOpClick(Event: TDOMEvent); Message;
  Private
    { Private declarations }
    Procedure GetMathOpResult(SoapClient : TSoapClient);
  Public
    { Public declarations }
End;
 
Implementation
 
Uses MathTestService;
 
Procedure Content.GetMathOpResult(SoapClient : TSoapClient);
Var
   MathOp: TMathProcessor;
Begin
     MathOp := TMathProcessor(SoapClient);
     Try
        TextLabel1.Caption := MathOp.theResult.ToString;
     Finally
        MathOp.Free;
     End;
End;
 
 
Procedure Content.btnExecuteMathOpClick(Event: TDOMEvent);
Var
   MathOp: TMathProcessor;
Begin
   MathOp := TMathProcessor.Create;
   MathOp.operand1 := Op1Edit.Text.ToInteger;
   MathOp.operand2 := Op2Edit.Text.ToInteger;
   MathOp.theOperator := 'ADD';
   MathOp.OnWebMethodReturn := @GetMathOpResult;
   MathOp.Execute;
End;
 
End.

The code that is presented in Listing 4 has two methods that exemplify the fundamentals of Web Service usage from the browser portion of a Morfik application. The first method is the btnExecuteMathOpClick method which is associated to the OnClick event of a button so that the user can directly trigger the execution of the request. This method instantiates the appropriate WebMethod class and sets its parameters before calling its Execute method.

Event though the Execute method will immediately return, it is important to remember that the actual Web Service call is asynchronous and will not return immediately. In order to receive a notification that the underlying Web Service being requested has returned the OnWebMethodReturn property of the Web Method that was instantiated is set to point to another method that takes a TSoapClient parameter.

In the case show in listing 4 this is the GetMathOpResult method. Inside this method the parameter is typecast into the type of the Web Method that was originally invoked and after its output parameters are read, the corresponding object is freed.

Related Topics

Back to top