Building Web Services

   

In Chapter 13 we saw the XML document capabilities of C++Builder 6 Enterprise. Now we're ready to examine another XML capability of C++Builder in the shape of SOAP and Web Services. It probably sounds more difficult than it needs to be, but will turn out to be easy, just wait.

SOAP stands for Simple Object Access Protocol, and is a cross-platform and cross-language protocol, which is not only supported in C++Builder 6, but also in Kylix 3 (for Linux), and lots of other tools.

SOAP Server Application

To build Web Services in C++Builder 6 we have to start with a SOAP Server Application. So, start C++Builder 6 Enterprise, File, New, Other, and go to the WebServices tab of the Object Repository. In this tab of the Object Repository, which can be seen in Figure 19.1, we see four icons: the SOAP Server Application , the SOAP Server Data Module , the SOAP Server Interface , and the WSDL Importer .

Figure 19.1. WebServices tab of Object Repository.

graphics/19fig01.gif

We'll use them all in this chapter, but at this time we need the first one, so select the SOAP Server Application icon and click OK. The New SOAP Server Application Wizard starts and asks about the type of Web server application that we want to use for our Web Service. In this chapter I want to use the simple CGI (common gateway interface) target, which can be seen in Figure 19.2. A CGI target is the easiest to deploy and use. See Chapter 22, "Web Server Programming with WebSnap," for more details about the different Web server application targets.

Figure 19.2. New SOAP server application.

graphics/19fig02.jpg

After you click on the OK button, a new SOAP Server project is created that includes a Web Module. We are also presented with a Confirm dialog, asking if we want to create an interface for the SOAP module (see Figure 19.3). If we answer Yes to this question, the SOAP Server Interface wizard is started (the third icon on Figure 19.1).

Figure 19.3. SOAP server Web module.

graphics/19fig03.gif

However, please just say no to this question at this time because I want to explain a few things that have been generated so far. Specifically, I want to explain the capabilities of the three components on the SOAP Web module, visible in the background of Figure 19.3. Don't worry about closing the Confirm dialog and not creating an interface for the SOAP module right away; we can always start the SOAP Server Interface wizard manually.

SOAP Server Web Module

Before we continue, save all files, placing the SOAP Web module in file SWebMod.cpp and the new SOAP project itself in BCB6WebService.bpr (this will result in a Web service Web server application called BCB6WebService.exe ).

The SOAP Web module already contains three components (that can also be found on the WebServices tab of the C++Builder 6 component palette), namely a THTTPSoapDispatcher , a THTTPSoapCppInvoker , and a TWSDLHTMLPublish component.

The THTTPSoapDispatcher component is used to receive incoming SOAP requests and dispatch them to another component (defined by its Dispatcher property) that can interpret the request and execute it. The latter will be done in this case by the THTTPSoapCppInvoker component, which receives the incoming SOAP request, executes (invokes) a C++ method, and produces the response back to the THTTPSoapDispatcher . Before the THTTPSoapCppInvoker can actually invoke the requested C++ method, it first checks to see if the method's interface and implementation class have been registered in the invocation registry (we'll get back to this in a moment).

Where the first two components are used when the Web service is actually consumed, the third component ” TWSDLHTMLPublish ”is used to produce the WSDL (Web Service Description Language) that defines the interface of the Web service itself.

We don't have to configure the properties or events of these three components; they are already fully prepared to be used. We only have to focus on the task at hand: Build some kind of Web service engine, and define its interface, so it can be published and used. In other words, we can now manually start the SOAP Server

Interface wizard. Open File, New, Other, go to the WebServices tab of the Object Repository, and double-click the SOAP Server Interface icon. This will present us with the Add New WebService dialog, where we can specify the name of the service as well as other options.

To get an idea what kind of functionality is suited to be packaged as a Web Service application, take a look at the list of Web services at http://www.xmethods.org. As a main example for this first section, I want to produce a Web Service that can convert inches to centimeters and back. It's only a small example, but will illustrate the Web Services functionality quite nicely (and it's actually even quite useful if you live in Europe and are used to centimeters, but want to estimate whether or not a 19-inch LCD screen will fit between the table desktop and the bookshelf ).

We should specify CmInch as Service Name (see Figure 19.4), which is automatically copied to the Filename identifier. That results in the files CmInch.cpp and CmInch.h , which will be generated and added to the project.

Figure 19.4. Generated SOAP server Web module.

graphics/19fig04.gif

In the Code Generation group we can select the option to generate comments (selected by default), or generate sample methods. You can select the latter option if you want, although in this section we will also write our own sample methods (which should be clear enough, so I generally do not need sample methods to be generated). The final option controls the service activation model, which I want to cover in a bit more detail now.

Server Activation Model

The choices for the service activation model are either "per request" (default) or "global." In the latter case, there will be one global Web Service instance to handle all incoming requests from clients . The default choice "per request" means that an instance of the implementation class is created for every incoming request (and destroyed after the request is handled). The code that is generated for the two choices differs in only one place ”a special Factory method is generated in case of the global choice. This Factory method checks if an instance already exists and returns it if this is the case, or creates the single instance if not. For our CmInch inter-face, the CmInchFactory() method would be implemented as follows inside CmInch.cpp :

 static void __fastcall CmInchFactory(System::TObject* &obj)  {    static _di_ICmInch iInstance;    static TCmInchImpl *instance = 0;    if (!instance)    {      instance = new TCmInchImpl();      instance->GetInterface(iInstance);    }    obj = instance;  } 

To make sure that this CmInchFactory() method is indeed used, it has to be passed as the second argument to the InvRegistry()->RegisterInvokableClass() method call, which is shown in this snippet from CmInch.cpp ):

 static void RegTypes()  {    InvRegistry()->RegisterInterface(__interfaceTypeinfo(ICmInch));    InvRegistry()->RegisterInvokableClass(__classid(TCmInchImpl), CmInchFactory);  } 

You might wonder why I showed these code snippets in such detail? The reason is to offer you the ability to switch between a "global" and a "per request" server instance by adding the CmInchFactory() method implementation and only adding the second argument to the InvRegistry()->RegisterInvokableClass() call when planning to use a global server instance. Otherwise, you can omit CmInchFactory as second argument and have a "per request" server instance.

So, my advice is to select the Global option for the Service Activation Model, and remove the second argument from InvRegistry()->RegisterInvokableClass() if you decide not to use a global instance after all (the code for the CmInchFactory won't be used in that case). Save the generated code in files CmInch.cpp and CmInch.h as indicated previously.

Invokable Registry

Let's backtrack for a moment here, and consider this InvRegistry object. This is the aforementioned Invokable Registry, used to register both the Web service interface (definition) as well as the implementation class. At the server side (which we are building at this time), both the interface and implementation have to be registered, of course, but at the client side (the topic of next section), only the Web service interface has to be registered to use it.

Web Service Interface

Now that we have an empty CmInch Web service skeleton, we should add some methods. Starting with the interface in CmInch.h , where we need to add two new methods, Cm2Inch() and Inch2Cm() , to the ICmInch interface definition as follows:

 // ************************************************************************ //  //  Invokable interfaces must derive from IInvokable  //  The methods of the interface will be exposed via SOAP  // ************************************************************************ //  __interface INTERFACE_UUID("{F446E03A-0BAE-437A-98A7-FF503836AE02}")     ICmInch : public IInvokable  {  public:    virtual float STDMETHODCALLTYPE Cm2Inch(float Cm) = 0;    virtual float STDMETHODCALLTYPE Inch2Cm(float Inch) = 0;  };  typedef DelphiInterface<ICmInch> _di_ICmInch; 

Now, before you hit F9 to compile ”this won't work, yet, because we've only defined the ICmInch interface (inside CmInch.h ). The TCmInchImpl class inside CmInch.cpp is the one to actually implement the ICmInch interface ”or at least the one that should implement it (which is why the code currently doesn't compile). So, switch back to the CmInch.cpp file and add the Cm2Inch() and Inch2Cm() methods to the class definition of TCmInchImpl (just copy them from the ICmInch definition and remove the "= 0" parts at the end). This leads to the following code for the class definition:

 // ************************************************************************ //  //  TCmInchImpl implements interface ICmInch  // ************************************************************************ //  class TCmInchImpl : public TInvokableClass, public ICmInch  {  public:    /* IUnknown */    HRESULT STDMETHODCALLTYPE QueryInterface(const GUID& IID, void **Obj)                        { return GetInterface(IID, Obj) ? S_OK : E_NOINTERFACE; }    ULONG STDMETHODCALLTYPE AddRef() { return TInterfacedObject::_AddRef();  }    ULONG STDMETHODCALLTYPE Release(){ return TInterfacedObject::_Release(); }    /* ICmInch */    virtual float STDMETHODCALLTYPE Cm2Inch(float Cm);    virtual float STDMETHODCALLTYPE Inch2Cm(float Inch);    /* Ensures that the class is not abstract */    void checkValid() { delete new TCmInchImpl(); }  }; 

Now the final step, the actual implementation of the Cm2Inch() and Inch2Cm() methods. For this we might want to look up the actual conversion rate between centimeters and inches, which is generally defined as 2.54 centimeters per inch. This means that the implementation of the two methods from our Web service class TCmInchImpl is as follows:

 const CmPerInch = 2.54;  float STDMETHODCALLTYPE TCmInchImpl::Cm2Inch(float Cm)  {    return Cm / CmPerInch;  };  float STDMETHODCALLTYPE TCmInchImpl::Inch2Cm(float Inch)  {    return Inch * CmPerInch;  }; 

At this time we can save all files, compile the project (and perhaps fix any typos that were made), and get ready to deploy the CmInch Web service on the Web.

Deploying the SOAP Server

Before we can deploy the Web service, however, we should first make sure that it can actually be executed on the target machine. By default, C++Builder 6 generates projects that use the dynamic RTL and runtime packages. Resulting in really small executables (the BCB6WebServices.exe is only 52,736 bytes), but requiring the dynamic RTL as well as a number of runtime packages. To turn it into a standalone executable (which is easy to install), we must perform a few additional steps.

First of all, we should open the Project, Options dialog, go to the Compiler tab and click on the Release button (just in case). Second, go to the Linker tab, and uncheck the Use dynamic RTL option. Third and last, go to the Packages tab and uncheck the Build with runtime packages option. Now close the Project Options dialog again, and recompile the application. This time, the BCB6WebService.exe will be 704,512 bytes big. A significant difference, but at least it can be deployed and used as a standalone CGI Web Service now, by placing it in a cgi-bin or scripts directory of a Web server similar to IIS (Internet Information Service). See Chapter 22, "Web Server Programming with WebSnap," for more deployment details.

Just for your convenience, I've deployed BCB6WebService.exe on my own Web server (hosted by TDMWeb), so it's now available to use as example and demonstration on the Web at http://www.eBob42.com/cgi-bin/BCB6WebService.exe (see also Figure 19.5.)

Figure 19.5. CmInch Web service on the Web.

graphics/19fig05.jpg

Note that this direct URL shows the two SOAP interfaces ICmInch and IWSDLPublish that are exposed by the BCB6WebService application. ICmInch is the one that we made, and IWSDLPublish is the one that every C++Builder Web service gets for free by using the TWSDLHTMLPublish component (see Figure 19.3).

We can click the link for each of these interfaces, click on their WSDL link, or pass the additional PathInfo/wsdl to the URL to get the WSDL (Web Service Description Language) description, automatically produced by the TWSDLHTMLPublish component, which is shown in Figure 19.6.

Figure 19.6. WebService Listing of BCB6WebService/wsdl .

graphics/19fig06.gif

If you click the link for the WSDL for ICmInch (or directly go to the URL http://www.eBob42.com/cgi-bin/BCB6WebService.exe/wsdl/ICmInch), you get the full WSDL for the ICmInch interface ”see Figure 19.7 for details.

Figure 19.7. WebService Listing of BCB6WebService/wsdl/IcmInch .

graphics/19fig07.jpg

NOTE

If you have a Web server on your own machine, the URL for the WSDL can be http://localhost/cgi-bin/BCB6WebService.exe/wsdl/ICmInch instead.


As can be seen, the WSDL is an XML document that consists of a number of sections such as the message, portType, binding, or service. We'll see some of these again shortly when we actually use the CmInch Web service.

In the next section of this chapter, we will use this WSDL, so we either need to save the WSDL as shown in Figure 19.7, or the URL itself to let C++Builder 6 itself retrieve the WSDL.


   
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net