link

# Developing Client Applications

# Introducing The Sample Code

To demonstrate how Web Services can be accessed in practice, Akixi provides the sample Java code that demonstrates some essential operations required to successfully use the API. The sample code is another recommended area for developers to investigate when working on Web Services API integration, as it covers steps like authentication, request invocation, exception handling, etc.

In this chapter, you will learn how to run sample projects provided by Akixi. It also covers recommendations and good practices for developing third-party applications that are meant to communicate with the Akixi Web Services API.

# Coding Conventions

Sample Java code demonstrating Akixi Web Services usage was developed in compliance with the coding guidelines described in the following sub-sections.

# Java Code Block Bracketing

A new code block’s opening parentheses is always started on a new line and has the opening & closing brackets horizontally aligned with the preceding owning statement.

# Class Names

For class declarations, the class name always begins with an upper case “C” followed by a reasonable shortened description of the class without underscores. Similar to variables names, class names should have the first letter uppercased for any full English words or terminology contained within the class name following the initial prefix.

# Variable Names

The Akixi variable naming style is based on a scheme originally evolved from Hungarian notation see https://en.wikipedia.org/wiki/Hungarian_notation - all variable names are prefixed according to variable scope first, followed by a secondary variable type prefix, finally followed by the rest of the variable name, which appropriately represents the use of the corresponding value.

Variable names should have the first letter uppercased for any full English words or terminology contained within the variable name following the initial prefix (the first letter of the prefix is always in lower case though). For further details, please refer to the table below:

# Primary Scope Prefixed
Scope Prefix Example
Static "s_" CSessionInfoMgr s_pSessionInfoMgr = null;
Member Variables "m_" CWSClientSettings m_pWSClientSettings = null;
Stack / Local [None] Thread pThread = null;
All Constants [None] public static final String sATTRVALUE_REQUEST_OPERATION_PARTITIONADD = "PartitionAdd";
# Type Prefixed
Scope Prefix Example
Primitive boolean "b" boolean bIsRequestSuccessful = true;
Primitive char "c" char cStringDataCur = CStrings.cCHAR_NULL;
Primitive int "i" int iErrorCode
Primitive long "l" long lTickcount = System.currentTimeMillis();
enums "i" or "e"
public enum eLOG_LEVEL
{
iLOG_LEVEL_FATAL,
iLOG_LEVEL_ERROR
};
String "s" String sPasswordHashSHA1 = CSecurity.HashSHA1(sPassword);
String sDevice;
Exception "pEx"
catch (Exception pEx)
{
// ... handling
}
Exception pExToThrow = new IllegalStateException();
Object "pObj" Object pObjSynchronize = new Object();
Java Primitive Type Object Wrappers "p" Integer pMapKeyYear = Integer.valueOf(iYear);
Long pKeyTime = new Long.valueOf(lTickcount);
Any Other Java Object "p" Thread m_pThread = null;
Thread m_pMainThread = null;
CLogging pLogging = null;
TimeZone pTimeZoneUTC;

# Property Names

Property names follow simple Java Beans property naming convention being prefixed with “get” & “set” for getter & setter methods accordingly. The first letter following the prefix should be capitalised, and then the same general rules apply to the following property name as for class names (first letter uppercased for any full English words or terminology contained within the class name, no underscores). Additionally, property methods should match their member variable counterparts without their variable scope & type prefixes.

# Method names

Normal (non-property orientated) methods follow the same capitalisation guidelines as property names, although without the “get” & “set” prefix. Most methods generally contain a verb term summarising the action that the method performs and a noun term to summarise the type of entity the method perform upon (unless the action is related purely to the owning object instance itself).

The noun being specified first is strongly preferred in method names followed by the verbal representation, although deviation is allowed when either a method represents an implementation of a Java or 3rd party specified interface, or the method is almost a property (i.e. “getObject(iIndex)” in a hypothetical collection-orientated class).

Use of the underscore character in method names must also be avoided.

# Comments

Classes, methods, variables and constants of the sample code provided by Akixi are all verbosely commented (unless the code fragment is short, self-describing, and what it does is very quickly obvious to an observer).

Sample code distributed by Akixi includes Javadoc-compatible comments for class names, method descriptions and for some commonly used constants.

“Placeholder” comments intended to indicate work to do or future required modifications are surrounded by a top & bottom comment lines containing the characters “###***”. The main encapsulated comment is prefixed by the text “To do:”.

Please note that placeholder comments are not used in the sample code itself, but can be found within this documentation (they describe logic that must be implemented between some sample code fragments).

# Installation Prerequisites

The sample code is distributed as a bundle of NetBeans IDE projects.

Although sample Java classes can be consumed by effectively any JDK environment, Akixi strongly recommends the use of the NetBeans IDE in order to test the sample code described in this chapter (as sample NetBeans projects were specifically designed to make Akixi Web Services operations runnable out of the box). Therefore, you can simply compile and run the sample project(s) via IDE’s user interface (without calling dependency management tools or manually resolving paths to some third-party Java libraries used by the sample logic).

To download NetBeans IDE, please follow the link http://netbeans.org/

The sample code has been successfully tested in versions 7.4 and 8.0.

Please note that if you are facing issues while testing the sample code in a different IDE, we strongly recommend re-running your test routines in NetBeans (to ensure that your issues are not related to unexpected dependency and/or project settings).

To open the sample Akixi Web Services Client Utilities project, please refer to the steps below:

  • Unarchive the contents of the sample code archive.
  • In NetBeans, navigate to the “Open Project” window (File - Open Project). Please tick the “Open Required Projects” tick box (if unticked).
  • Select the unarchived WSClientUtils project and click the “Open Project” button. You will now see two sample projects (WSClientUtils and PublicUtils) in your project area. If you open the sample code for the first time, you will most likely see a notification stating that “One or more project resources could not be found”:

This error normally occurs when Netbeans fails to find the project’s default JDK version. To resolve this problem, right-click on the affected project and open the “Properties” window. Then, navigate to the “Libraries” category and select a usable Java platform from the “Java Platform” drop-down list (please refer to the screenshot below).

Sample projects described in this chapter support JDK 1.6 and above. If you can’t see any available JDKs in the “Java Platform” list, please ensure that you have a valid JDK installation on your computer. Please refer to this website to learn more about different JDK versions: (http://www.oracle.com/technetwork/java/javase/downloads)[http://www.oracle.com/technetwork/java/javase/downloads]

  • If all project-related problems were successfully resolved, you will be able to test the sample Web Services project. This can be done by accessing sample classes and methods from the main(String[] args) method of your own proprietary calling logic implemented within a separate Java SE Application project.

Please note the Akixi sample code is distributed as a set of Java Class Libraries without an executable class/method (i.e. it is a standalone set of utility classes, not a directly executable Java application). Please refer to the following sub-sections below in order to learn about the primary classes & methods that must be invoked from your own executable Java class/method.

# Sample Code Structure

As mentioned above, the sample source code is distributed as a bundle of Netbeans IDE Java projects. System requirements and other project-related details are summarised in the table below:

Project Name JDK/JRE Version Description External Imports
WSClientUtils 1.6+ Includes sample logic demonstrating common client- side operations like authentication, request invocation and response processing.
This project uses pre-generated JAX-WS stubs in order to consume and process information about the Web Service. Therefore, requests to Web Services can be built in a clean and straightforward way (as it is not required to generate XML messages from scratch - you can simply manipulate data structures that represent actual request parameters covered within this document).
WSWSDL.jar
PublicUtils
PublicUtils 1.6+ Includes common utilities used by publicly available Akixi projects.
Sample code provided within this project covers some essential authentication/encryption-related logic mentioned in this documentation (like password hash generation and client-side password encryption). Therefore, it is highly recommended to review the corresponding classes & methods before implementing your own authentication-related client-side procedures.
In addition to client-specific utilities, this project also includes some useful utilities and constants for certain widely used tasks (e.g. string manipulation, marshalling, exception handling, etc).
Apache Commons Codec 1.3
Apache Commons Lang 2.3
Log4j 1.2.17

External imports required to successfully build & run sample projects are distributed within their associated project bundles. If for some reason you decided to download a third-party library directly from the corresponding Library Provider, please ensure that only the library versions listed in the table below are used (as earlier/later versions might not be compatible with the sample code provided by Akixi).

Library Name / Version Version Description
WSWSDL.jar 1.0.0.1 This library is effectively a JAX-WS client stub generated by an artefact generation tool called wsimport (generation tools read WSDL definitions and generate corresponding class structures). Classes of this library are used to build clean and straightforward requests by referring to actual Web Services structures described in this documentation (instead of building XML messages from scratch).
Please note that class structures of this library fully depend on the current WSDL implementation. Therefore, if the Akixi WSDL file has been updated, the library must be regenerated in order to include recent changes. Otherwise, client stubs will conflict and therefore the client implementation will become unusable.
Akixi will ensure that all updates of the Web Services API will be followed by releasing updated versions of this library (in order to make it compatible with the latest WSDL version).
Please also note that you can try regenerating this library by yourself (i.e. for research purposes). This can be done by running artefact generation tools like wsimport and providing the correct WSDL location. However, it is recommended to use the latest version of the library distributed by Akixi instead.
Apache Commons Codec 1.3 Apache Commons Codec library provides implementations of common encoders and decoders such as Base64, Hex, Phonetic and URLs.
Apache Commons Lang 2.3 Apache Commons Lang library provides a host of helper utilities for the java.lang API, notably String manipulation methods, basic numerical methods, object reflection, concurrency, creation and serialization and System properties.
Log4j 1.2.17 Apache Log4j is a logging library for Java. It can be either used directly, or through the Akixi-specific CLogging wrapper class provided within the PublicUtils project.

Please note that if you test the sample code by creating a new project that refers to WSClientUtils and PublicUtils projects, you will also have to include all libraries listed above into your newly created project’s imports (i.e. your standalone project’s imports must include all external libraries listed above, as well as the Akixi sample projects).

Logic responsible for sending requests, processing responses and handling errors is implemented within multiple classes and packages. However, if you only want to utilise the sample code for authenticating and invoking basic Web Services requests, you only need to familiarise yourself with the following two primary classes (note that they must be instantiated from your calling logic in order to perform all WS-related operations):

Class Name Package Description
CWSClientSettings com.Akixi.Public.WSClientUtils Immutable settings class that specifies all communication properties required to invoke Web Services requests and process server-side responses.
This class can be instantiated via multiple constructor variants. Detailed constructor variants allow you to fully customise most communication properties used by the sample client. However, you must only use detailed constructor variants if you are entirely sure that the default settings provided by Akixi are not appropriate. Akixi strongly recommends to use the default constructor for the sample test scenario described in this document.
Default settings can be assigned via minimalistic constructor (that only requires mandatory input arguments like host address of the Akixi Service).
Once created, client settings are used to initialise a new CWSClientUtils class instance.
CWSClientUtils com.Akixi.Public.WSClientUtils This is effectively the core class of the sample client-side logic provided by Akixi, as it allows you to authenticate, invoke requests, receive server-side data and perform other essential operations.
Communication settings used by this class must be specified within a CWSClientSettings instance (please note that the settings class must be initialised before instantiating the client itself).

# Initialising Client Settings

The code fragment below demonstrates how to instantiate communication-related settings required to connect to the Web Services endpoint.

// Sample host address of the Akixi Service.
final String sAKIXI_HOST_ADDRESS = "example.akixi.com";
// Initialise client settings.
        CWSClientSettings pClientSettings = new CWSClientSettings(sAKIXI_HOST_ADDRESS);

In this example, client settings are initialised through the default constructor. Therefore, only a host address argument value is required to instantiate the class instance. However, you can choose from various constructor variants that allow you to customise most communication-related settings. Please note that the default constructor variant uses optimal settings (e.g. timeouts and schema- related parameters) that can be used to run most test cases. Therefore, communication parameters must be only modified if you want to deliberately change the default behaviour.

# Initialising Client Utilities

The following code fragment demonstrates how to instantiate the class that provides sample client- side functionality required to invoke Akixi Web Services API requests:

// Initialise the sample client class by providing required communication settings. 
// Once initialised, the object instance can be used in order to invoke Akixi Web 
// Services requests.
CWSClientUtils pClientUtils = new CWSClientUtils(pClientSettings);

In order to initialise the sample class, you must simply pass the CWSClientSettings instance that was described in the paragraph above. The sample Web Services utilities class can be used to invoke all currently supported Web Services operations (by providing the designated API request operation name and the list of request parameters required to successfully execute that particular operation).

# Authentication

The code fragments below demonstrate how to authenticate and sign out via the sample client-side logic provided by Akixi.

 // Define Akixi Web API access username.
final String sAKIXI_USERNAME = "SampleClient@akixiprovider.com";
// Define Akixi Web API access password.
final String sAKIXI_PASSWORD = "p@ssword4W3bS3rv1c3s";

// Firstly, we must create a new session.
// In this example, all session-related resources returned by this operation
// are stored within the CWSSessionInfo class instance.
// You can access actual session-related values via corresponding getter methods 
// (or just pass this instance to subsequent authentication-related requests). 
        CWSSessionInfo pSessionInfo = pClientUtils.CreateSession();

// Most Akixi features are only available to authenticated users.
// Therefore, you will need to authenticate the session against your credentials 
// before calling administrative requests.
// To authenticate, you must specify correct Akixi Credentials and session-related 
// values. This sample operation returns the authenticated Web Session Identifier. 
// Once authenticated, it must be specified within all subsequent requests.
        String sAuthenticatedSessionID = pClientUtils.AuthenticateSession(
        sAKIXI_USERNAME, sAKIXI_PASSWORD, pSessionInfo);

// ###***
// To do: Invoke further Web Services requests here.
// Actual request examples are provided later on within this document.
// ###***

// Finally, sign the user out.
// You should immediately sign out if your client-side logic no longer requires
// an active API session.
// Signing out will deactivate your Session Identifier - therefore, you
// would need to subsequently create a new session and authenticate once again
// in order to invoke further Akixi requests. 
        pClientUtils.SignOut(sAuthenticatedSessionID);

The AuthenticateSession() method is effectively a wrapper for various utility methods demonstrating authentication-related routines described in chapter 4 (“Authentication”). Normally, you need four input arguments in order to authenticate a session:

  • Username
  • Password
  • Session Identifier
  • Session Nonce

The two last arguments (i.e. Session Identifier and Nonce) can be either provided separately (as String values), or within a single CSessionInfo instance (it allows you to easily pass all session-related values to the AuthenticateSession() method variant).

All requests provided in this chapter are called via their minimalistic method variants. Therefore, in this example, you don’t have to provide Invoke Identifiers (tokens that are used to uniquely identify concurrent API requests) from your calling logic - Instead they will be generated automatically by the active CWSClientUtils object instance.

To learn more about the implementation of the multi-digest password generation routine within the Akixi sample code, please refer to static method MultiDigestPasswordGenerate() within the com.Akixi.Public.Utils.Security.CSecurity class of the PublicUtils project.

# Invoking Web Services Requests

The code example below demonstrates how to invoke a PartitionInfo request through the sample code provided by Akixi.

 // Operation name.
final String sOPERATION_NAME = "PartitionInfo";
// Request parameter name (Telephony System Identifier).
final String sREQUEST_PARAMETER_NAME_TELSYSID = "TelSysID";
// Request parameter value (Telephony System Identifier).
final String sREQUEST_PARAMETER_VALUE_TELSYSID = "1";
// Request parameter name (Partition's unique identifier within its Telephony System). 
final String sREQUEST_PARAMETER_NAME_PARTIDINTELSYS = "PartIDInTelSys";
// Request parameter value (Partition's unique identifier within its Telephony System). 
final String sREQUEST_PARAMETER_VALUE_PARTIDINTELSYS = "1";

// Firstly, create a new Container.
// Containers include request-related child elements (e.g. lists of request properties 
// or nested containers).
        ContainerType pContainerType = new ContainerType();

// Secondly, obtain the list of properties using the corresponding getter method.
// Please note that automatically generated JAXB stubs don’t support setter methods for 
// the PropertyOrContainer type. Therefore, we obtain the live reference to the list in 
// in order to add new properties.
        List<Object> pListRequestProperties = pContainerType.getPropertyOrContainerOrListContainer();
// Property instances store actual request parameters.
        PropertyType pPropertyTypeTelSysID = new PropertyType();
        PropertyType pPropertyTypePartIDInTelSys = new PropertyType();

// Populate partition properties.
//
// Set Telephony System Identifier. 
        pPropertyTypeTelSysID.setName(sREQUEST_PARAMETER_NAME_TELSYSID);
        pPropertyTypeTelSysID.setValue(sREQUEST_PARAMETER_VALUE_TELSYSID);
// Set Partition Number within the owning Akixi Telephony Server configuration component. 
        pPropertyTypePartIDInTelSys.setName(sREQUEST_PARAMETER_NAME_PARTIDINTELSYS);
        pPropertyTypePartIDInTelSys.setValue(sREQUEST_PARAMETER_VALUE_PARTIDINTELSYS);

// Add properties to the list of request parameters.
        pListRequestProperties.add(pPropertyTypeTelSysID);
        pListRequestProperties.add(pPropertyTypePartIDInTelSys);
// Actually invoke the operation. 
// 
// Return type of this operation will include requested Partition-related details. 
        ContainerWithAttachmentType pResponseContainerWithAttachmentType = pClientUtils.InvokeOperationWithPayloadProperties(pContainerType, sOPERATION_NAME,
        sAuthenticatedSessionID);

In this example, we request information about the Akixi Partition component using its unique identifiers (Telephony Server Identifier as well as its Partition Number within the owning Akixi Telephony Server configuration component).

Parameter names & values for the Partition component identifier values are declared as constants at the top of the code fragment given above. These are used to configure all the necessary parameters required to successfully invoke the API request, where these parameters are set as individual Properties of a Container instance.

This operation’s response gives a Container that specifies Partition-related details. You need to parse the Container’s contents in order to extract the returned Partition-related information. Please refer to the next sub-section in order to learn more about parsing Web Services responses via the sample code.

# Reading Web Services Responses

The Java code fragment below demonstrates how to read a response of the previously invoked PartitionInfo request, via the sample code distributed by Akixi.

// A PartitionInfo request returns Partition-related details.
// In this example, Partition information will be read and stored as a hashmap. 
Map<String,String> pResponseProperties = new HashMap<String,String>();
// The response includes objects that are presumably Containers.
        if ((pResponseContainerWithAttachmentType != null) &&
        (pResponseContainerWithAttachmentType instanceof ContainerType))
        {
        // Firstly, resolve the response Container.
        ContainerType pResponseContainerType = (ContainerType)pResponseContainerWithAttachmentType;
        // Secondly, obtain the list of Properties.
        List<Object> pResponseContainer = pResponseContainerType.getPropertyOrContainerOrListContainer();
        if ((pResponseContainer != null) && !pResponseContainer.isEmpty())
        {
        // Iterate through Container's content. 
        // 
        PropertyType pPartitionProperty; String sResponsePropertyName;
        String sResponsePropertyValue;
        Object pObject;
        Iterator<Object> pIterator = pResponseContainer.iterator();
        while (pIterator.hasNext())
        {
        // Objects provided on this level are separate Partition Properties.
        pObject = pIterator.next();
        if ((pObject != null) && (pObject instanceof PropertyType))
        {
        pPartitionProperty = (PropertyType)pObject;
        // Get the property name.
        sResponsePropertyName = pPartitionProperty.getName();
        // Get the property value.
        sResponsePropertyValue = pPartitionProperty.getValue();
        // Add Partition Properties to the hashmap (as key/value pairs). 
        pResponseProperties.put(sResponsePropertyName, sResponsePropertyValue);
        }
        }
        }
        }

In this example, we parse the Container that includes response properties (as key/value pairs) and save them into a map for further usage.

Please note that not all Akixi Web Services requests will return a Container - responses of some operations will only i nclude the operation’s result completion status (e.g. just the Result attribute and the <InvokeID> element, as well as error information if appropriate).

# Handling Attachments

Some operations will return an attachment as part of the response. They consist of 3 main attributes:

Operation Name Operation Description
Filename This is simply the name of the attached file (including the file extension) and can be ignored if you would like to use a custom name.
Content Type Also known as the MIME type, this is the two-part identifier that informs the client of what type of file is being sent. An example of a MIME type is text/csv, where the type is text and the subtype is csv.
Value This is the actual binary file data to be saved to the file.

In order to easily save these attachments there is a method AttachmentSave in the CWSClientUtils class contained within the com.Akixi.Public.WSClientUtils package of the WSClientUtils project. To use it you provide it with the attachment you would like to save and the path to the directory in which you would like to save it (ensure this ends with a file path separator in order to keep it separated from the filename). This will save the attachment to the directory with the filename given by the attachment’s filename value. There is another method variant that allows you to additionally provide a custom filename (if the filename provided is null or empty it will behave in the same manner as the previously mentioned variant).

The Java code fragment below demonstrates how to use this to save an attachment from the response of a BillingReportGet request.

// Define folder used to save attachments in.
final String sSAVELOCATION = "C:\\Billing Reports\\";
// Get list of attachments.
        List<AttachmentType> pAttachments = pResponseContainerWithAttachmentType.getAttachment();
// Check for attachments.
        if (!pAttachments.isEmpty())
        {
        // Save attachments.
        for (AttachmentType pAttachment : pAttachments)
        {
        // Create file.
        File pFile = pWSClientUtils.AttachmentSave(pAttachment, sSAVELOCATION);
        }
        }
# Message Transmission Optimization Mechanism (MTOM)

When a response contains an attachment it is normally sent in the main body as a Base64 encoded string, which has the side effect of increasing the data volume by ~33%. This can be avoided by enabling MTOM on the client (see http://docs.oracle.com/middleware/12212/wls/WSGET/jax-ws-mtom.htm for more info), which sends the attachment as an unencoded MIME message at the end of the SOAP response and replaces the Base64 encoding with a placeholder. It is preferred that MTOM is enabled to speed up transfers, but it is possible that Base64’s extra interoperability is necessary, in which case MTOM should be disabled.

# Handling Errors

The Akixi sample code demonstrates how various errors can be gracefully handled and converted into readable error messages via the CWSClientReqRespException class contained within the com.Akixi.Public.WSClientUtils package of the WSClientUtils project.

In general, the sample logic returns two overall categories of errors:

Error Type Description
Inner Exceptions Or Request Submission Failures This is when getExceptionType() returns anything else apart from CWSClientReqRespException.iERROR_WSCLIENT_WSLOGIC_ERROR.
These categories of error are thrown if the sample logic fails to send a request, or if an unexpected internal error has occurred. For example, if you specify an empty Akixi host address, the internal validation logic will throw an exception that will include a readable message stating that the host address cannot be empty. If you lose your Internet connection while sending a request, it will also result in an exception.
The sample code provides readable error messages for most commonly thrown exceptions. Note that all generated readable messages are non-locale specific (i.e. always in UK English).
Server-Side Errors (API Specific Errors) This is when getExceptionType() specifically returns CWSClientReqRespException.iERROR_WSCLIENT_WSLOGIC_ERROR.
If your request successfully reaches the endpoint but fails due to a server-side error, you will receive a server-side error message (this includes an API specific error code and a readable text message obtainable via the getWSErrorCode() & getMessage() methods respectively).

Both the above error categories are handled by the CWSClientReqRespException class. The code fragment below demonstrates how you can use this class to retrieve server-side error details.

try
        {
        // ###***
        // To do: Invoke your Web Services requests here.
        //        Actual request examples can be found in the sub-section(s) above.
        // ###***
        }
// Handle possible Web Services errors.
        catch (CWSClientReqRespException pEx)
        {
        // Perform mitigating logic based on the returned exception category type.
        //
        int iExceptionType = pEx.getExceptionType();
        if (iExceptionType == CWSClientReqRespException.iERROR_WSCLIENT_WSLOGIC_ERROR)
        {
        // Resolve the Web Services API error code.
        //
        int iWSErrorCode = pEx.getWSErrorCode();
        if (iWSErrorCode == CWSClientConstants.iERRORCODE_11111_DUPLICATEPARTITIONID)
        {
        // ###***
        // To do: Gracefully handle this error or display an error message. 
        // ###***
        }
        else if (iWSErrorCode == CWSClientConstants.iERRORCODE_10307_USERLOCKEDOUT)
        {
        // ###***
        // To do: Display an error message. 
        // ###***
        }
        else
        {
        // Unexpected API error. Verbosely log it for subsequent investigation
        // activities if required.
        }
        //
        }
        else if (iExceptionType == CWSClientReqRespException.iERROR_WSCLIENT_INNER_EXCEPTION)
        {
        // Obtain inner exception.
        Throwable pExInner = pEx.getCause();
        //
        // ###***
        // To do: Investigate inner exception, and perform appropriate error handling. 
        // ###***
        }
        else
        {
        // Unexpected error. Verbosely log it for subsequent investigation activities
        // if required.
        }
        }

In this example, server-side error codes are mapped to client-side constants. This allows you to resolve error details and gracefully handle errors (by displaying custom error messages or invoking further Web Services requests that can possibly resolve the problem).

Inner exceptions and request submission failures can be processed in a similar way (using the corresponding getExceptionType() getter method). If an unexpected error/exception has occurred, it is strongly recommended to generate logging output for it. Normally, inner exceptions and server-side errors include detailed error messages. However, you could also potentially provide custom error messages by writing a wrapper for the CWSClientReqRespException exception class.

# Roadmap For Developing Client Applications

This paragraph lists some recommendations for developers working on Web Services client implementations.

# Refer To The Provisioning System Implementation Guidance

This page includes implementation guidance within topic “Provisioning System Implementation Guidance”, which describes how to implement programmatic logic for automatically updating the Akixi Service configuration from Telephony Provider or Reseller provisioning systems. It is highly recommended to design and implement your provisioning logic according to recommendations provided within chapter 9.

# Refer To The Sample Code

It is highly recommended to review the sample code provided by Akixi before writing your own client implementations. However, client applications and modules used in production environments may require a more complex design and structure. Although the sample code includes good practices for some essential routines (e.g. for authentication-related operations, request invocation or error handling), developers working on Web Services client implementations are fully responsible for choosing the most appropriate approach (which might be different from the approach demonstrated in the sample code). Therefore, Akixi strongly recommends to design client implementations from scratch and only reuse the sample code if you fully understand its purpose and possible limitations.