C++Builder 5 Developers Guide

   

It's time to import and use ”also called consume ”Web services. To get an idea what kinds of functionality are suited as Web Service applications, I always recommend taking a look at the list of Web services http://www.xmethods.org, as mentioned previously. In the next section, we will consume a Web service written in a foreign language, but right now we should start by consuming the CmInch Web services we wrote in the previous section using C++Builder 6 itself.

A Web Service consumer can be any kind of application. But to keep things simple, let's just start a new default visual application in C++Builder. Save the form in file MainForm.cpp and the project in BCB6WSClient.bpr . Now we have to add an import unit that represents the CmInch Web service interface ”a job for the WSDL Importer.

WSDL Importer

We need to use the WSDL Importer (from the WebServices tab of the Object Repository), because we must import the WSDL that defines the Web service (interface) and generate an C++ import unit that defines the C++ interface of this particular Web service. So, activate File, New, Other, go to the WebServices tab of the Object Repository, and double-click the WSDL Importer icon, which will result in the WSDL Import wizard (see Figure 19.8).

Figure 19.8. WSDL importer.

Enter the URL of the Web Service that we've just created. This can be http://localhost/cgi-bin/BCB6WebService.exe/wsdl/ICmInch (on your own localhost machine) or the "real" http://www.eBob42.com/cgi-bin/BCB6WebService.exe/wsdl/ICmInch (for the version deployed on my Web site on the Internet). If you've saved the WSDL in a local file, you can also click on the button with the ellipsis and specify the location of the local file with the WSDL for ICmInch . The benefit of using a local WSDL file is that you don't need an active Internet connection to develop the Web service client application, and you can edit the file and make some changes to it if you want (and know what you're doing).

After we've specified the URL to retrieve the WSDL information for our Web Service, we can take a look at the options by clicking the Option button (see Figure 19.9).

Figure 19.9. WSDL Importer (Options).

We can also click the Next button to extract structure information from the WSDL and display this information in a treeview as well as show the code that will be generated, which is shown in Figure 19.10.

Figure 19.10. WSDL Importer (Final Page).

As you can see, the ICmInchservice (Web service) root consists of one interface, called ICmInch , which has two methods : Cm2Inch() and Inch2Cm() . This information was extracted from the retrieved WSDL and not from anything else (the WSDL Importer has no knowledge about what we did in the previous section of this chapter). Note that the WSDL Importer will also work with Web Services that are written in an entirely other development environment (as we will see in the next section), although some interoperability issues between SOAP implementations in other environments may remain to be worked on.

Anyway, we can now click the Finish button to create the Web Service import unit. Save the generated import unit in file ICmInch.cpp , and make sure to #include the ICmInch.h header file in the MainForm.h header file (after the other #include statements in MainForm.h ) as follows :

#include "ICmInch.h"

The generated unit ICmInch.h has the following contents for the definition of the ICmInch Web service interface:

// ************************************************************************ // // The types declared in this file were generated from data read from the // WSDL File described below: // WSDL : http://www.ebob42.com/cgi-bin/BCB6WebService.exe/wsdl/ICmInch // Version : 1.0 // (2002-08-15 12:23:22 - $Revision: 1.0.1.0.1.82 $) // ************************************************************************ // #ifndef ICmInchH #define ICmInchH #include <System.hpp> #include <InvokeRegistry.hpp> #include <XSBuiltIns.hpp> #include <SoapHTTPClient.hpp> namespace NS_ICmInch { // ************************************************************************ // // The following types, referred to in the WSDL document are not being // representedin this file. They are either aliases[@] of other types // represented or were referredto but never[!] declared in the document. // The types from the latter categorytypically map to predefined/known // XML or Borland types; however, they could also // indicate incorrect WSDL documents that failed to declare or import a schema // type. // ************************************************************************ // // !:float - "http://www.w3.org/2001/XMLSchema" // *********************************************************************** // // Namespace : urn:CmInch-ICmInch // soapAction: urn:CmInch-ICmInch#%operationName% // transport : http://schemas.xmlsoap.org/soap/http // style : rpc // binding : ICmInchbinding // service : ICmInchservice // port : ICmInchPort // URL : http://www.ebob42.com/cgi-bin/BCB6WebService.exe/soap/ICmInch // *********************************************************************** // __interface INTERFACE_UUID("{CDD9B184-9089-99F2-FFD3-3BD586315394}") ICmInch : public IInvokable { public: virtual float Cm2Inch(const float Cm) = 0; virtual float Inch2Cm(const float Inch) = 0; }; typedef DelphiInterface<ICmInch> _di_ICmInch; _di_ICmInch GetICmInch(bool useWSDL=false, AnsiString addr=""); #endif // __ICmInch_h__ }; // NS_ICmInch #if !defined(NO_IMPLICIT_NAMESPACE_USE) using namespace NS_ICmInch; #endif

Note that the definition of the ICmInch interface looks very much like the definition we wrote in the CmInch.cpp unit of the previous section (for the BCB6WebService project). That isn't too strange , of course, if you consider that the entire purpose of the WSDL Importer Wizard is to indeed regenerate the interface (in C++ code).

Even more interesting to look at is the file ICmInch.cpp , which contains the code to get an instance of the ICmInch Web service interface by calling the GetICmInch() method. See Listing 19.1.

Listing 19.1 Generated Import Unit for ICmInch Web Service

[View full width]

// ************************************************************************ // // The types declared in this file were generated from data read from the // WSDL File described below: // WSDL : http://www.ebob42.com/cgi-bin/BCB6WebService.exe/wsdl/ICmInch // Version : 1.0 // (2002-08-15 12:23:22 - $Revision: 1.0.1.0.1.82 $) // ************************************************************************ // #include <vcl.h> #pragma hdrstop #if !defined(ICmInchH) #include "ICmInch.h" #endif namespace NS_ICmInch { _di_ICmInch GetICmInch(bool useWSDL, AnsiString addr) { static const char* defWSDL= "http://www.ebob42.com/cgi-bin/BCB6WebService.exe/wsdl/ ICmInch"; static const char* defURL = "http://www.ebob42.com/cgi-bin/BCB6WebService.exe/soap/ ICmInch"; static const char* defSvc = "ICmInchservice"; static const char* defPrt = "ICmInchPort"; if (addr=="") addr = useWSDL ? defWSDL : defURL; THTTPRIO* rio = new THTTPRIO(0); if (useWSDL) { rio->WSDLLocation = addr; rio->Service = defSvc; rio->Port = defPrt; } else { rio->URL = addr; } _di_ICmInch service; rio->QueryInterface(service); if (!service) delete rio; return service; } // ************************************************************************ // // This routine registers the interfaces and types used by invoke the SOAP // Service. // ************************************************************************ // static void RegTypes() { /* ICmInch */ InvRegistry()->RegisterInterface(__interfaceTypeinfo(ICmInch), L"urn:CmInch-ICmInch", L""); InvRegistry()->RegisterDefaultSOAPAction(__interfaceTypeinfo(ICmInch), L"urn:CmInch-ICmInch#%operationName%"); } #pragma startup RegTypes 32 }; // NS_IcmInch

Using ICmInch

Now, move back to the (empty) main form, and drop two TLabeledEdit components (call them edCm and edInch ), and two TButton components (call them btnCm2Inch and btnInch2Cm ). Set the EditLabel->Caption property of edCm to centimeters, and the EditLabel->Caption property of edInch to inches. Set the Caption properties of btnCm2Inch and btnInch2Cm to Cm to Inches and Inches to Cm, respectively.

Now that the GUI part is done, we need to drop a THTTPRIO component ”the left-most component from the Web Services tab of the Component Palette. The THTTPRIO component is a Remote Invokable Object that communicates using HTTP (hence the name HTTPRIO ). It will be used by a client application to connect to a Web Service and pretend to implement the Web Services at the client location. To the client, it's as if the THTTPRIO component is behaving as if its a local implementation, whereas, in fact, the THTTPRIO is connecting to the remote Web Service, sending SOAP requests and receiving SOAP answers over HTTP.

This might sound complex, but working with the THTTPRIO component is really easy. Set the WSDLLocation property of the THTTPRIO component to the location of the WSDL, which can be found at either http://localhost/cgi-bin/BCB6WebService.exe/wsdl/ICmInch or http://www.eBob42.com/cgi-bin/BCB6WebService.exe/wsdl/ICmInch (note that the later will be much slower than using the localhost because it requires your THTTPRIO to connect to the Internet for every SOAP request).

TIP

When I started to enter a URL for the WSDLLocation property, I experienced a form of Code Completion because after the first h I immediately got the entire URL for ICmInch for the WSDLProperty. Quite handy, although I'm not sure if this is an official feature.

Now, set the Service property of the THTTPRIO component to ICMInchservice (the only choice if you click the arrow for the drop-down combo box). Finally, set the Port property to ICMInchPort , which is again the only possible choice. This should enable the design of the Web Service client form, which can be seen in Figure 19.11.

Figure 19.11. Centimeters to inch converter Web Service client.

We now need to write the code for the BtnCm2Inch and BtnInch2Cm buttons . We start with BtnCm2Inch first, using the THTTPRIO component and use QueryInterface to extract the _di_ICmInch interface from it. After that, we can use the method Cm2Inch() from this interface, as if it was a simple local method. See the following code snippet:

void __fastcall TForm1::btnCm2InchClick(TObject *Sender) { _di_ICmInch service; HTTPRIO1->QueryInterface(service); if (service) { edInch->Text = FloatToStr(service->Cm2Inch( StrToFloatDef(edCm->Text,0))); } }

You can now save, compile, and run the BCB6WSClient application and confirm that you can convert centimeters to inches (but not back, yet).

Apart from using the three WSDLLocation , Service , and Port properties, we can also use just one property: the URL property. This property can be set to the same value as we specified for the WSDLLocation property, with the difference that we have to specify /soap instead of /wsdl . So, in our example, that becomes http://www.eBob42.com/cgi-bin/BCB6WebService.exe/soap/ICmInch. Placing this value in the URL property will clear the WSDLLocation , Service , and Port properties. Recompile the BCB6WSClient application, and you can confirm that you can still convert centimeters to inches.

You might wonder what the difference is between using the single URL property or the WSDLLocation , Service , and Port properties. According the online help, the WSDLLocation , Service , and Port properties are used when you need to dynamically look up connection information from the WSDL document at runtime. If you don't need that, you can use the URL property instead, which can result in clients that execute a bit faster.

Without HTTPRIO

If you think this was easy, watch this: We can even do it without the THTTPRIO component. As you might have noticed, the ICmInch.cpp file contains a function called GetICmInch() , which creates a THTTPRIO component behind the scenes. We can even pass arguments to the GetICmInch() function, which is the first one to specify the use of the WSDLLocation , Service , and Port properties versus the URL property (by default set to use of the URL property). It is the second to specify the address where the Web service can be found. For the conversion of inches to centimeters we can use the GetICmInch() function and obtain the Inch2Cm() method from the resulting _di_ICmInch interface, as in the following code:

void __fastcall TForm1::btnInch2CmClick(TObject *Sender) { edCm->Text = FloatToStr(GetICmInch()->Inch2Cm( StrToFloatDef(edInch->Text,0))); }

After you compile and run the client application, you can enter inches and convert them to centimeters (or vice versa). You can also use C++Builder to consume one of the numerous other available Web Services, or think and produce another Web Service yourself. The sky and your imagination are the limits.

In the next section, we will use an existing Web service available today on the Internet, which provides a bit more functionality than simply converting centimeters to inches (something that we could do a lot faster without Web services in the first place).


   
Top

Категории