Web Services Framework API: Using the API

  • Web Services Framework API: Using the API
    • Checking if Active Scheduler is started
    • Registering a service information
      • ID-WSF
      • Basic Web Services
    • Creating a connection
      • ID-WSF
      • Basic Web Services
    • Setting complete server messages on/off
    • Sending a request and receiving a response
      • Asynchronous function CSenServiceConnection
      • Synchronous function CSenServiceConnection
    • Handling a response - XML Parsing
      • Namespaces
        • About namespaces
        • Use of CSenElement
      • XML Parsing
        • Using CSenDomFragment
        • Using CSenSoapMessage
    • Closing a connection
    • Error handling
    • Memory Overhead
    • Extensions to the API

A Web service consumer implements the MSenServiceConsumer interface and its three callback methods:

  • virtual void?HandleMessageL (const TDesC8 &aMessage)=0
    • Used for receiving a response to a submitted request. The parameter aMessage represents the incoming message.

    • Note that the Web service consumer may set whether or not it wishes to receive complete SOAP envelopes (see the Setting complete server messages on/off section).

  • virtual void?HandleErrorL (const TInt aErrorCode, const TDesC8 &aMessage)=0
    • This function gets called when an error occurs. The parameter aErrorCode specifies the error and the parameter aMessage describes the error.

  • virtual void?SetStatus (const TInt aStatus)=0
    • This function is called when the status of the connection changes. The parameter aStatus is a connection state indicator, which could be specified by the actual framework implementation. The following status codes (defined in SenServiceConnection.h) are possible for any installed framework:

    • KSenConnectionStatusNew: When the connection is created but not yet ready to use.

    • KSenConnectionStatusReady: The connection is initialized and ready to be used.

    • In addition, ID-WSF may notify about KSenConnectionStatusExpired. This is the case if the connection has expired.

      Web Services Framework API: Using the API - jianhai1229 - 水木博客

      Figure 6:  Web service consumer implements the MSenServiceConsumer interface to use WSF API

Typically, the use of a Web service is done in seven phases:

  1. Checking if Active Scheduler is started

  2. Registering a service information

  3. Creating a connection

  4. Setting complete server messages on/off

  5. Sending a request and receiving a response

  6. Handling a response - XML Parsing

  7. Closing a connection

See the following sections for detailed instructions on how to use the interfaces.

S60 SDK contains the example application using Web Service Framework called AddressBook.

Checking if Active Scheduler is started

Before using WSF, the Web service consumer should check that the active scheduler is started. This can be done by calling CActiveScheduler::Current(). If the return value is NULL, the scheduler should have been started. Notice that before starting the scheduler by calling CActiveScheduler::Start() an active scheduler object should have been instantiated. The created instance should be installed by calling CActiveScheduler::Install(CActiveScheduler* )and some active object should have been created and added to the active scheduler by calling CActiveScheduler::Add(CActive*). At least one request function should also have been issued, otherwise the thread will hang.

Registering a service information

Service-related data is registered using Service Manager. Note that Service Manager is not framework-specific. In Basic Web Services, the consumer application’s only applicable operation is managing identity data. This can be used to make connection specific Basic Authentication (BASIC-AUTH) credentials available without later end-user interaction (otherwise the required authentication information is requested via notifier dialog prompts).

An ID-WSF consumer has to use Service Manager to register a service description and identity provider and finally associate these two together.

Service Manager is meant to be used before the developer creates a connection to a certain service.

It is highly recommended that the password (credential) is registered only when the WSC application is started for the first time. Otherwise, if the password is being changed by the end user at some point, that will be overwritten byMSenServiceManager::RegisterIdentityProviderL() passing the password value. To accomplish this, the actual WSC application should have this knowledge (i.e. the initial password being already registered) serialized into some persistent storage (such as the file system or a database).

ID-WSF

In ID-WSF’s case, the developer does the following sequence:

  1. Instantiates Service Manager (CSenServiceManager).

  2. Instantiates an Identity Provider (CSenIdentityProvider) and registers it using the Service Manager instance. Note that if an IDP with the same endpoint and provider ID exists, any properties, including the credentials (such as password) are updated.

  3. Associates the actual service to the identity provider using the appropriate service ID.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 7:  Registering ID-WSF

The following example describes the Service Manager usage in the ID-WSF case. iManager is an instance variable and will be destroyed in the destructor:

// Literals (usually found in a header file)  _LIT8(KASEndPoint,   "http://selma.ndhub.net:9080/tfs/IDPSSO_IDWSF");  _LIT8(KASContract,   "urn:liberty:as:2004-04");  _LIT8(KASProviderID, "http://selma.ndhub.net:9080/tfs/");  _LIT8(KAuthzID,      "testuser1");  _LIT8(KPassword,     "testuser1");  _LIT8(KSPContract,   "urn:nokia:test:addrbook:2004-09");  // Creating an instance of CSenServiceManager  iManager = CSenServiceManager::NewL();  CSenIdentityProvider* idp = CSenIdentityProvider::NewLC(      KASEndPoint, KASContract);  // ProviderID must be unique  idp->SetProviderID(KASProviderID);  // Associate Forum Nokia's Addressbook service ID (contract)  // to this Identity Provider  idp->SetServiceID(KSPContract);  // Set Liberty ID-WSF framework ID  idp->SetFrameworkIdL(KDefaultIdWsfFrameworkID);  // ------------------------------------------------------------------------  // The following username/password properties will be used for  // authentication. Please note, that using advisory authentication  // id of "IMEI" will result device ID to be directly fetced from phone.  // ------------------------------------------------------------------------  //  idp->SetUserInfo(KAuthzID, KNullDesC8, KPassword);  // ------------------------------------------------------------------------  // The next, (commented) line below would cause password to be  // prompted from user through dialog presented by Serene:  // idp->SetUserInfo(KAuthzID, KNullDesC8, KNullDesC8);  // ------------------------------------------------------------------------  //  // Registering the IdP to the WSF  TInt retVal = iManager->RegisterIdentityProviderL(*idp);  if(retVal != KErrNone)       {       // Error occurred. If there was ProviderID conflict, the application       // could of course unregister the conflicting IDP using same      // ProviderID and re-attempt registrate its own. But generally,      // ProviderIDs should be checked to be unique.       }  // Register this IDP to be authentication service, too  retVal = iManager->RegisterServiceDescriptionL(*idp);  CleanupStack::PopAndDestroy(); // idp  

Liberty IDP consists of a unique provider ID and some endpoint. Any service that is known to trust certain IDP should have its unique service ID associated with this IDP. IDP may also require authentication credentials. In the above code, the last two API calls register this information about an IDP, which is trusted by an Address book service. Later, when a service connection to this identity consuming Address book service is requested (by telling its contract only) the WSF knows one endpoint more where to search for the identity provider. This means that the WSF IDP registry is shared among all service consumer applications.

Note that when registering an IDP, there is no need to specify a framework ID because it defaults to “ID-WSF”. Also, the default contract is "urn:liberty:as:2004-04" (Liberty authentication service) so there is no need to set it either. Register an authentication service description using the above identity provider instance.

After the last association call, the connection can be initialized.

Basic Web Services

In a basic WS case, the sequence is a bit different. Service Manager (and any of its functionalities) is not needed at all if the developer is not connecting to a service that is behind basic authentication. If basic authentication is required, the developer can perform the following steps to avoid the authentication dialog to be popped:

  1. Instantiate Service Manager (CSenServiceManager)

  2. Instantiate an identity provider (CSenIdentityProvider) and register it using the Service Manager instance created in step1. Note that if an IDP with the same endpoint exists, any properties, including credentials (such as username and password) are overwritten.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 8:  Registering Basic WS

The following example describes the Service Manager usage in the Basic Web Service (WS-I) case. iManager is an instance variable and will be destroyed in the destructor:

// Creating an instance of CSenServiceManager  CSenServiceManager* pManager = CSenServiceManager::NewLC();  // Creating an instance of IdP and registering it. Notice, that endpoint is the // endpoint to the actual Web Service Provider.  CSenIdentityProvider* pIdp = CSenIdentityProvider::NewLC(KWSPEndPoint());  // Set BASIC-AUTH credentials  pIdp->SetUserInfo(KUser, KUser, KPass);  // Set FrameworkID (constant declared in SenServiceConnection.h)  pIdp->SetFrameworkID(KDefaultBasicWebServicesFrameworkID);  TInt error = pManager->RegisterIdentityProviderL(*pIdp);  CleanupStack::PopAndDestroy(2); // pManager, pIdp  

Each WS-I service has its unique authentication endpoint acting as an identity provider. Therefore, no provider ID needs to be specified.

Creating a connection

A service connection has to be initialized before it can be used. The initialization process starts by creating a service connection object. Getting a connection is an asynchronous operation, meaning that the connection is ready when the client’s SetStatus() callback function gets called with the status code KSenConnectionStatusReady. Note that actually no requests have yet been sent to the WSP at this stage. This means that the Web service consumer cannot rely on the existence of the Web service provider and therefore the requests may fail. In ID-WSF, the connection may also expire.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 9:  Connection’s state

The creation of a connection is done by creating a CSenServiceConnectionobject using the provided CSenServiceConnectionor CSenServiceConnectionfactory methods. The client registers itself as an observer by passing a reference to itself (*this) as the first parameter to the factory methods for the callback functions presented in the MSenServiceConsumerinterface:.

virtual void HandleMessageL(const TDesC8& aMessage) = 0;  virtual void HandleErrorL(const TInt aErrorCode, const TDesC8& aMessage) = 0;  virtual void SetStatus(const TInt aStatus) = 0;  

These functions are the callback functions for receiving the messages, errors and status data of the service connection. These functions are used in the connection creation phase and when sending messages to the WSP.

TheCSenServiceConnection or CSenServiceConnection factory methods can be used in three ways by changing the second parameter:

  • By passing a contract (an URN) in a descriptor

  • By passing a reference to MSenServiceDescription

  • By passing a reference to a CSenServicePatternobject

In the connection creation phase (iConnection is instantiated), the execution goes the following way:

  1. Instantiating CSenServiceConnection. This creates an asynchronous call to the WSF server and the execution is returned to the calling application almost immediately.

  2. The server processes the connection creation. It connects to the authentication service and requests the wanted service endpoint from the discovery service. In the client-side, the execution is in the application using WSF. Usually, the client application shows a progress bar to the user meaning that the server is processing the request.

  3. When the server has finished the request, it calls the SetStatus() callback function with KSenConnectionStatusReady. The call means that the WSP endpoint is discovered and that the credentials to the service are in valid state.

  4. The client application can now start the transaction with the WSP using the CSenServiceConnectionand CSenServiceConnectionfunctions. CSenServiceConnection is asynchronous and therefore the response from the server is received through the HandleMessageL() callback function. CSenServiceConnection is synchronous and therefore the execution is blocked in the client-side until the message is received from the WSP.

ID-WSF

The cases between ID-WSF and Basic Web Services differ also a bit. This section describes the actions that are faced when using ID-WSF:

  1. Instantiate a service description that is used to create the connection. The contract to be set to the service description must be the same as is associated to the IDP in the registration phase (see the ID-WSF section).

  2. Create a service connection using the service description instantiated and initialized in step 1.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 10:  Creating Connection for ID-WSF

The following example describes the connection creation using the ID-WSF. iConnection is an instance variable and is destroyed in the destructor. This code makes it possible to connect to an ID-WSF service. When this code is executed for the first time, authentication and discovery services are connected and the user is therefore authenticated. When this code is executed again and the credentials received from the authentication and discovery services are valid, there is no need to connect to them again.

_LIT8(KSPContract,   "urn:nokia:test:addrbook:2004-09");  // Instantiating the Service Description  CSenXmlServiceDescription* pattern = CSenXmlServiceDescription::NewLC();  // Setting the contract. Notice that this contract must be the same that was  // associated in the registration phase (ServiceType)  pattern->SetContractL(KSPContract());  iConnection = CSenServiceConnection::NewL(*this, *pattern);  CleanupStack::PopAndDestroy(); // pattern  

The consumer is only required to know the contract of the service. WSF then attempts to resolve the endpoint to the identity providing the service and to utilize credentials to discover the service.

Basic Web Services

In the Basic Web Services case, the connection creation differs from the ID-WSF case only in a few places.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 11:  Creating Connection for WS-I

The following example describes the connection creation in the Basic Web Services case:

// Instantiates the SD used for creating the connection.  // If the application is connecting to a service that is behind basic  // authentication the KWSPEndPoint literal must be the  // same that was used in the IDP registration.  CSenXmlServiceDescription* pPattern =      CSenXmlServiceDescription::NewLC(KWSPEndPoint(), KNullDesC8());  // This FrameworkID setting tells the WSF that a basic WS is connected.  // KBasicWebServicesFrameworkID is the FrameworkID for Basic Web Services  // as defined in SenServiceConnectin.h.  pPattern->SetFrameworkIdL( KBasicWebServicesFrameworkID() );  // Construct the connection. Here this same class has also implemented  // the required callback functions from MSenServiceConsumer interface.  iConnection = CSenServiceConnection::NewL( *this, *pPattern );  CleanupStack::PopAndDestroy(); // pPattern  

The WS-I framework is selected by setting the framework ID into the service description instance. The endpoint of the actual service is known in advance.

The callback functionality is the same as in the ID-WSF case. The only difference is the speed of the connection initialization. Because the authentication and discovery services are not connected, the SetStatusL()callback function call with KSenConnectionStatusReady is received immediately. This means that the application can start the transaction with the WSP using the CSenServiceConnection and CSenServiceConnection-functions.

Setting complete server messages on/off

The Service Consumer sets the messages received to be complete (on) or incomplete (off). In the case of incomplete messages, only the actual response from the service is delivered to the service consumer.

Calling CSenServiceConnection causes the client to receive the whole SOAP message. Otherwise, the response only contains the service-specific content (the SOAP message body).

Note: Calling this function must not be done before connection is initialized (the observer's SetStatus() has been called with value KSenConnectionStatusReady). Calling this function should be done before sending or submitting anything.

In WSF frameworks the default settings are:

  1. In ID-WSF, the complete server messages is OFF.

  2. In Basic Web Services, the default is ON.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 12: Setting complete server messages on/off

The following example describes the setting complete server messages in on/off by the example of the ID-WSF.

_LIT8(KSPContract,   "urn:nokia:test:addrbook:2004-09");  // Instantiating the Service Description  CSenXmlServiceDescription* pattern = CSenXmlServiceDescription::NewLC();  // Setting the contract. Notice that this contract must be the same that was  // associated in the registration phase (ServiceType)  pattern->SetContractL(KSPContract());  iConnection = CSenServiceConnection::NewL(*this, *pattern);  // Setting the flag in ON to receive the whole SOAP message  TInt error = iConnection->CompleteServerMessagesOnOff(ETrue);  CleanupStack::PopAndDestroy(); // pattern  

Sending a request and receiving a response

After the connection has been established (the client application has received a notification to the SetStatus() function), a call to the specific service can be done by using one of the two functions CSenServiceConnection or CSenServiceConnection.

If an error occurs, the client’s HandleErrorL() function is called. Finally, the connection should be released by deleting the connection object.

Asynchronous function CSenServiceConnection

It has two versions:

  1. CServiceConnection::SendL(const TDesC8& aRequest)

  2. CSenServiceConnection

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 13:  Using the SendL() function

If CSenServiceConnectionSendL() is used, the client application’s callback function HandleMessageL() gets called after the services response can be delivered to the client.

The following example describes the sending request in asynchronous case:

_LIT8(KSPContract,   "urn:nokia:test:addrbook:2004-09");  // Instantiating the Service Description  CSenXmlServiceDescription* pattern = CSenXmlServiceDescription::NewLC();  // Setting the contract. Notice that this contract must be the same that was  // associated in the registration phase (ServiceType)  pattern->SetContractL(KSPContract());  iConnection = CSenServiceConnection::NewL(*this, *pattern);  // Setting KRequest  _LIT8(KRequest, "<ab:Query xmlns:ab=\"urn:nokia:test:addrbook:2004-09\"><ab:QueryItem><ab:Select>/ab:Card[containts[ab:N/ab:FAMILY,\'Bob\'] or containts[ab:N/ab:GIVEN,\'Bob\'] or containts[ab:TEL,\'Bob\']]</ab:Select></ab:QueryItem></ab:Query>");  iConnection->SendL(KRequest);  CleanupStack::PopAndDestroy(); // pattern  

Synchronous function CSenServiceConnection

It has two versions:

  1. CSenServiceConnection

  2. CSenServiceConnection

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 14:  Using the SubmitL() function

In the case of CSenServiceConnection, the response is passed in the second parameter (aResponse) for which the necessary memory is allocated. If the pointer points to allocated memory, the memory is deleted. The user is responsible for the deletion of the parameter (the change of ownership).

The following example describes the sending request in synchronous case:

_LIT8(KSPContract,   "urn:nokia:test:addrbook:2004-09");  // Instantiating the Service Description  CSenXmlServiceDescription* pattern = CSenXmlServiceDescription::NewLC();  // Setting the contract. Notice that this contract must be the same that was  // associated in the registration phase (ServiceType)  pattern->SetContractL(KSPContract());  iConnection = CSenServiceConnection::NewL(*this, *pattern);  // Setting the request  _LIT8(KRequest, "<ab:Query xmlns:ab=\"urn:nokia:test:addrbook:2004-09\"><ab:QueryItem><ab:Select>/ab:Card[containts[ab:N/ab:FAMILY,\'Bob\'] or containts[ab:N/ab:GIVEN,\'Bob\'] or containts[ab:TEL,\'Bob\']]</ab:Select></ab:QueryItem></ab:Query>");  HBufC8* pResponse = NULL;  iConnection->SubmitL(KRequest, pResponse);  if(pResponse)    {    CleanupStack::PushL(pResponse);    iLog->Log( _L("Received response:") );    LogResultL( *pResponse );    CleanupStack::PopAndDestroy(); // pResponse    }   else    {    iLog->Log( _L(" Response NULL! ") );       }  CleanupStack::PopAndDestroy(); // pattern  

Handling a response - XML Parsing

Once the service consumer has received the response from the service, the response may be handled using WSF’s helper classes. The response is a descriptor containing the SOAP message or at least its body (depending on the complete server messages setting, see the Setting complete server messages on/off section) returned from the service. The response may be parsed using either CSenSoapEnvelope (from the Service Connection library) or by using XML Extensions directly.

One thing to notice in XML parsing is the size of the document. CSenDomFragment (from the XML Extensions library) builds an in-memory DOM tree from the document. If this approach is too memory-consuming, then the parsing has to be done as in SAX. This is shown in the AddressBook example, that contains in S60 SDK.

Usually the user of this component (the XML client application) inherits CSenBaseFragmentand overrides the necessary methods from MSenContentHandlerClient. The client also creates an instance of CSenXmlReader. CSenBaseFragmentcontains the methods to set the created CSenXMLReader and to start the parsing. The receiver (the content handler) of the callbacks defined in MSenContentHandlerClientcan also be switched (this can be called as delegating the parsing). The client may also implement additional classes inherited from CSenBaseFragmentto which the parsing may be delegated.

Note that by using CSenBaseFragment, the attributes in the XML document’s elements are not available but namespaces are. Instead, by using CSenDomFragment, the attributes (and also namespaces) are available.

Namespaces

About namespaces

Each CSenBaseElement object has an array of local namespaces (which it owns). It also has one pointer, which points to the default namespace in the namespace array. When an element is added, the namespaces are checked. If its ancestors already have any of these namespaces in their local namespace arrays, the child’s namespaces are removed. This way each namespace exists only once.

When an element gets detached, its namespace is constructed as follows:

If the elements are:

<root xmlns=”namespace”> <a> <b> </b> </a> </root>  

and a or b are detached, then these elements would have the default namespace “namespace”.

If the elements are:

<pre:element xmlns:pre=”something”> <pre:a> <b> </b> </pre:a> </pre:element>  

and the a element gets detached, it will contain its prefix (“pre”). If b gets detached, it will not have a namespace.

Use of CSenElement

CSenElement checks the namespace declarations of the child and its new parent. If the child does not have a default namespace but the new parent has, the method applies (maps) the parent’s default namespace to apply to this child.

Secondly, if the child has some other namespace declarations (which are found from the parent), the method removes its own (local allocation) and points to the parent’s namespace object instead.

The behaviour explained above is supposed to be done using a parent. For example, if someone calls the parent's AddElementL() giving a child as an argument, then the child element is added to the parent and SetParent() is also called as in the base element implementation:

CSenElement& CSenBaseElement::AddElementL(CSenElement& aElement)      {      User::LeaveIfError(iElements.Append(&aElement));      aElement.SetParent(this);      return aElement;      }  

The SetParent call also prevents multiple declarations of the same namespace.

XML Parsing

Using CSenDomFragment

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 15:  Using CSenDomFragment class

The following example describes how the client application constructs CSenDomFragment and parses an XML document. Then the contents of the elements are printed to the console. The handling of the DOM tree is done in the ListNodesL() function. ListNodesL() receives an array of elements, the console object and the current level in the tree. ListNodesL() calls itself recursively for any element in the array which has children. The parsing and calling ListNodesL() is done in the TestBaseXmlParsing() function.

TInt TestBaseXmlParsing(CConsoleBase &aConsole)      {       TInt retVal(KErrNone);      _LIT(KText,  "\n\n\n\n\n------------------\nTesting\nCSenDomFragment\n");      _LIT(KPressKey, " PRESS ANY KEY...\n");      aConsole.Printf(KText);      aConsole.Printf(KPressKey);      aConsole.Getch();         // create new XML reader and push it to the cleanupstack       CSenXmlReader* reader = CSenXmlReader::NewL();       CleanupStack::PushL(reader);        //create a CSenDomFragment       CSenDomFragment* pBase = CSenDomFragment::NewL();       CleanupStack::PushL(pBase);        //the XML document to be parsed       _LIT(KToBeParsed, "c:\\input.xml");        //open a filesession      RFs fss;      User::LeaveIfError(fss.Connect());      CleanupClosePushL(fss);      //must set the content handler  ´   reader->SetContentHandler(pBase);      // and the reader      pBase->SetReader(*reader);      //do the parsing      reader->ParseL(fss, KToBeParsed);        //after the parsing, get the elements:      RPointerArray<CSenElement>& array = pBase->AsElement().ElementsL();        //start the listing of the elements, first level is 1      ListNodesL(array, aConsole, 1);      CleanupStack::PopAndDestroy(3); // pBase, fss, reader      return retVal;      }    // The listing of the elements is done in ListNodesL function:  void ListNodesL(RPointerArray<CSenElement>& aArray,      CConsoleBase &aConsole, int aLevel)      {      _LIT(KNewLine, "\n");      _LIT(KNoContent,"NO CONTENT");      _LIT(KDepthFormatString, "lev%d: ");        TInt size = aArray.Count();      for(TInt i=0; i<size; i++)          {          aConsole.Printf(KNewLine);          //get an element          CSenElement* pElement = aArray[i];          TBool hasActualContent = EFalse;          if( pElement->HasContent() )              {              //get the content of the element              TPtrC8 content = pElement->Content();                //to a HBufC, notice "LC"              HBufC *pContent = SenXmlUtils::ToUnicodeLC(content);                //print current depth (“level”)              aConsole.Printf(KDepthFormatString, aLevel);                //trim possible whitespace characters              pContent->Des().Trim();              //if there is content after the trimming              if( pContent->Length() > 0)                  {                  hasActualContent = ETrue;                  aConsole.Printf(*pContent);                  }                //destroy pContent              CleanupStack::PopAndDestroy(pContent);              }          if( !hasActualContent)              {              aConsole.Printf(KNoContent);              }            //get the first child          CSenElement* child = pElement->Child(0);            //check if element has childs          if( child )              {              //get the child elements              RPointerArray<CSenElement>& tree = pElement->ElementsL();                //list child elements by a recursive call              ListNodesL(tree, aConsole, ++aLevel);              }          aLevel--;          } //for loop ends      }  

By default, CSenDomFragmentinstantiates a tree of new element objects representing the parsed XML document. This is resource-consuming and not a reasonable approach to most of the XML parsing objectives. Instead, one should use Base fragments and concentrate on emergent sections of such XML.

For example, if the document to be parsed is the following:

<?xml version="1.0"?>  <root>  <level1>   Level1 value   <level2 attrib="value">    Level2 value    <level3>Level3 value</level3>   </level2>   <level2>    Level2 value2    <level3>      Level3 value     <level4>Level4 value</level4>    </level3>   </level2>   <level2>Level2 value3</level2>  </level1>  </root>  

The output of the example when run in the emulator is shown in the Example run in the emulator figure below.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 16:  Example run in the emulator

Using CSenSoapMessage

CSenSoapMessageinherits CSenSoapEnvelope. The only thing CSenSoapMessageadds is the use of the SOAP Security header. It has been implemented in CSenWsSecurityHeader, and an instance of it is as a member of CSenSoapMessage. The example below demonstrates the use of CSenWsSecurityHeaderby finding out if there is a Security header.

A service consumer might use CSenSoapEnvelopein parsing the responses from the service. The consumer might not receive the whole Envelope but just the Body (this can be set by calling CSenServiceConnection). In the example below, this has been taken into consideration.

One special case is the SOAP Fault. In the example below, a check is performed to see if the Envelope has a Fault element.

Web Services Framework API: Using the API - jianhai1229 - 水木博客

Figure 17:  Using CSenSoapMessage class

In the TestSoapMessage function, CSenSoapMessageis constructed with some test data (a KSOAPMessage literal). After the parsing, a check is made to see if a whole Envelope was made. In this case, the ProcessReceivedCompleteSoapMsgL() function gets called. Otherwise, if there is only the Body, the ProcessReceivedSoapMsgBodyL() function gets called.

void TestSoapMessage(CConsoleBase& aConsole)      {      //To test Envelope with a security header      _LIT8( KSOAPMessage, "<Envelope       xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\"><Header><wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"><UsernameToken><Username>name</Username></UsernameToken></wsse:Security></Header><Body ><TestRequest xmlns=\"Just:checking:and:testing\"><Test>testing</Test></TestRequest></Body></Envelope>" );        //first the message is parsed      CSenXmlReader *pXmlReader = CSenXmlReader::NewL();      CleanupStack::PushL(pXmlReader);      CSenSoapMessage* pMessage = CSenSoapMessage::NewL();      CleanupStack::PushL(pMessage);      pMessage->SetReader(*pXmlReader);        TRAPD(leaveCode, pMessage->ParseL(KSOAPMessage));      if(leaveCode!=KErrNone)          {          CleanupStack::PopAndDestroy(2); //pMessage, pXmlReader          return;          }      //if there is a complete envelope then there are actual elements      TInt elements = pMessage->AsElement().ElementsL().Count();      if( elements > 0 )          {           aConsole.Printf(_L("Got complete SOAP envelope\n"));           ProcessReceivedCompleteSoapMsgL(*pMessage, aConsole);           }      else //if not, then we have a detached SOAP body           {           ProcessReceivedSoapMsgBodyL(KSOAPMessage, aConsole, *pXmlReader);          }          CleanupStack::PopAndDestroy(2); //pMessage, pXmlReader      }    // The processing of the whole element starts by checking, if a security header  // was received. Next check reveals, whether the message is a SOAP Fault, which  // could be extracted from the SOAP Body.  void ProcessReceivedCompleteSoapMsgL(CSenSoapMessage& aMessage, CConsoleBase         &aConsole)      {      //There’s no other way to check for security header      CSenElement& headers = aMessage.HeaderL(); //header element      ProcessSecurityHeaderFromHeader(headers, aConsole);      //First check if there is a Fault      if(aMessage.IsFault())          {          //includes SOAPFault          //  note: by calling aMessage.FaultL() the ownership would NOT be taken          //  CSenSoapFault* pFault = aMessage.FaultL();          //By calling DetachFaultL() the ownership IS taken:          CSenSoapFault* pFault = aMessage.DetachFaultL();          CleanupStack::PushL(pFault);          if(pFault)              {              //Process the SOAP Fault              HBufC8* pFaultAsXml = pFault->AsXmlL();              CleanupStack::PushL(pFaultAsXml);              pFaultAsXml->Length() );                TPtrC8 faultCode = pFault->FaultCode();              TPtrC8 faultString = pFault->FaultString();              TPtrC8 faultActor = pFault->FaultActor();              TPtrC8 detail = pFault->Detail();       //Do something with values of this SOAP Fault              CleanupStack::PopAndDestroy(); // pFaultAsXml              }          CleanupStack::PopAndDestroy();//pFault          }          //Next process the SOAP Body          MSenElement *pBody = aMessage.BodyL().AsElement();          HBufC* pUnicode = pBody->AsXmlUnicodeL();          //Do something with the Body          delete pUnicode;      }  

Intelligent consumer applications could apply a similar logic in their HandleErrorL() implementation. If the completeServerMessages mode is off, only SOAP message body or SOAP fault is received. Typically, applications only need to implement handling for either of the modes, but both the options are processed in the example above.

The processing of the Security header is done in ProcessSecurityHeaderFromHeader. CSenSoapMessagedoes not provide an easy way to get the Security header, so in the example below, it is searched using the Security element’s namespace and name. Then, CSenWsSecurityHeaderis constructed and the data is copied. Finally, the “username” token from the Security element is created for the username “dummy”.

void ProcessSecurityHeaderFromHeader(CSenElement& headers, CConsoleBase &aConsole)      {      _LIT8(KSecurityName, "Security");      _LIT8(KSecurityXmlNs, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");      _LIT8(KSecurityXmlNsPrefix, "wsse");        //Search for the Security element      //Ownership of pSec is not transferred. NULL is equal to “not found”.      CSenElement* pSec = headers.Element(KSecurityXmlNs, KSecurityName);      if( pSec )           {          CSenWsSecurityHeader *pSecH = CSenWsSecurityHeader::NewL();           CleanupStack::PushL(pSecH);            //Copy the data          pSecH->AsElement().CopyFromL(*pSec);            HBufC8* pToken = NULL;          CleanupStack::PushL(pToken);            //Get the token for the given name          pSecH->UsernameTokenL(_L8("dummy"), pToken);            //Now the token would be processed.          CleanupStack::PopAndDestroy(); //pToken          CleanupStack::PopAndDestroy(); //pSecH          }      }  

If only the Body of a SOAP message is processed, the ProcessReceivedMessageBodyL function is executed. There the Body is parsed using CSenDomFragment. Then a check for a SOAP Fault is done.

void ProcessReceivedMessageBodyL(const TDesC8& aResponse, CConsoleBase &aConsole,CSenXmlReader& aXmlReader )      {      //Parse the body      CSenDomFragment* pDom = CSenDomFragment::NewL();      CleanupStack::PushL(pDom);      pDom->SetReader(aXmlReader);        TRAPD(error,pDom->ParseL(aResponse) );      if(error!=KErrNone)          {          CleanupStack::PopAndDestroy(); //pDom          return;          }      _LIT8(KFaultName,"Fault" );      _LIT8(KQName, "soapenv:Fault");        //Check if there is a Fault      //Note: ownership not transfered      CSenElement *pFault = pDom->AsElement().Element(KFaultName);      if( pFault ) //There is a fault          {          CSenSoapFault* pFault2 = CSenSoapFault::NewL( *pFault );          CleanupStack::PushL(pFault2);            if(pFault2)              {              HBufC8* pFaultAsXml = pFault2->AsXmlL();              CleanupStack::PushL(pFaultAsXml);              TPtrC8 faultCode = pFault2->FaultCode();              TPtrC8 faultString = pFault2->FaultString();              TPtrC8 faultActor = pFault2->FaultActor();              TPtrC8 detail = pFault2->Detail();                //Do something with the values of this SOAP Fault              CleanupStack::PopAndDestroy(); // pFaultAsXml              }          CleanupStack::PopAndDestroy(); // pFault2          }      CleanupStack::PopAndDestroy(); //pDom      //Now SOAP Body could be processed.      }  

Closing a connection

Closing a connection is done simply by deleting the connection object.

Error handling

Some of the following possible error situations should be taken into consideration:

  • Cannot establish a connection.

  • A connection has been established, but the request fails.

  • Basic authentication fails in Basic Web Services.

  • Authentication and discovery errors in case of ID-WSF.

  • Expiration of an ID-WSF connection.

  • The Web service provider responds with SOAP Fault.

The consumer is notified of errors by calling the HandleErrorL() function.

One thing to remember to do is the registration of the service and identity related data via Service Manager.

Memory Overhead

Using the XML Extensions library for parsing may cause a lot of overhead depending on the size of the XML document.

Extensions to the API

The Web Services API does not explicitly support any kind of extensions to it.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值