com.atlassian.confluence.content.render.xhtml.migration.exceptions.UnknownMacroMigrationException: The macro 'next_previous_links' is unknown.

Consuming Services Using Stubs

A Javascript stub is built specially for a service and serves two primary purposes:

  • Data binding - When appropriate, the stub converts XML to Javascript data types.
  • Abstract binding details - The stub takes care of all the details of formulating the message that are sent to the endpoint.

When you import the stub into a Javascript environment (the Carbon server or a web page shown in a browser), a global object called 'services' is created with a property as the name of the service. That is, for a service named "helloworld", importing the helloworld stub will create a global object called "services". By manipulating the properties and calling the methods of this object, messages can be sent to an endpoint and the responses can be received.

Invoke Service Operations

The 'services' global object has the property 'service name' and that property has attributes such as operations exposed by that web service, end point details etc. Format of the global object 'services' for 'helloWorld' service is as follows.

services={   
    "admin/helloWorld": {   
      "$":{   
      }  
      "operations":{  
            "hello": {  
                  response:{}  
                  payloadJSON:{}   
                  payloadXML:{}  
                  callback  
                  onError  
             }  
        }  
     }  
} 

According to the above global object, there's a property name as service name "admin/helloWorld", and it contains two attributes as operations and $. The "operations" attribute contains some more attributes as response, payloadJSON(), payloadXML(), callback() and onError().

Out of these response, payloadJSON() and payloadXML() are defined in an imported stub and you have to define other two methods in your consuming client.The property - serviceName of the global object 'services' can be accessed as services.serviceName, but in case if the serviceName contains "." or "/", it will not work. Therefore, the serviceName is accessed as follow:

var theResponse = services["admin/helloWorld"];

For instance, if the "helloworld" service has a "hello" operation, after importing the stub you can invoke the hello operation like this:

var theResponse = services["admin/helloWorld"].operations["hello"]; 

Wrapping this method with a try-catch block to define a recovery scenario for the operation is recommended, if in case an error is thrown such as Internet connection inaccessibility.

try {  
    var theResponse = services["admin/helloWorld"].operations["hello"];  
} catch (e) {  
    alert("Danger Robinson! " + e);  
}

Setting Endpoints and Endpoint Addresses

The global object corresponding to a service has a property and two methods which help you determine the binding to use in order to invoke the service:

  
{services["serviceName"]}.$.endpointThis read/write property, names the endpoint that will be invoked. This corresponds to the WSDL 2.0 endpoint name - and for services exposed by the service, it is generally one of these values:
  • "SOAP12Endpoint"
  • "SOAP11Endpoint"
  • "HTTPEndoint"
  • "SecureSOAP12Endpoint"
  • "SecureSOAP11Endpoint"
  • "SecureHTTPEndpoint"
{services["serviceName"]}.$.getAddress(endpoint)This method takes an endpoint name (typically one of the above) and returns the endpoint address.
{services["serviceName"]}.$.setAddress(endpoint, url)This method sets the address for a particular named endpoint.
{services["serviceName"]}.$.scriptInjectionCallbackThis read/write property defines an optional callback when an operation is invoked, providing a hook for notification when a normal Web Service call is made versus an attempt at accessing an endpoint across domains within a browser environment. The callback parameter indicates whether script injection was used (true) or whether a normal Web service call was performed (false).

myMashup.scriptInjectionCallback = function(scriptInjection) {
if (scriptInjection) alert("Using script injection");
else alert("Normal Web services call");
}

You will use the "endpoint" property to select an alternate binding, if the default (SOAP 12) isn't appropriate for some reason. You might use the 'getAddress' and 'setAddress' properties to reroute the message, for instance, to a monitoring program such as tcpmon (which displays the messages and forwards them to their original destination.)

// choose an endpoint type  
services["serviceName"].$.endpoint = "HTTPEndpoint";  
  
// record where the messages were supposed to go  
log("Original destination: " + services["serviceName"].$.getAddress("HTTPEndpoint"));  
  
// redirect the messages to a different port for logging/debugging purposes.  
services["serviceName"].$.setAddress("HTTPEndpoint", "http://localhost:12345/services/myService"); 

Asynchronous Invocation

Because Web Services often involve communicating with services from diverse locations, it is a best-practice to make the call asynchronously. In the browser, this prevents the UI from blocking and becoming unresponsive. It offers better performance because you can invoke several operations in parallel rather than waiting for one to complete before starting the next. Asynchronous programming can be a bit more complicated than regular synchronous calling, but the benefits are usually well worth the additional complexity. The stubs support asynchronous calling through the addition of two properties (callback, onError) on the method objects:

var helloWorld = services["admin/helloWorld"].operations["hello"];  
var payload = null;  
helloWorld.callback = function(payload) {  
    var responseXML = WSRequest.util._serializeToString(payload);     
};  
helloWorld.onError = handleError;  
helloWorld(payload);   
function handleError(error) {  
    log (console, "Fault: " + error.reason + "\n\n" + error.detail);  
};

The callback and onError handlers, when set to a function, cause the operation to be executed asynchronously. That is, the call is sent, and the execution continues (displaying the "waiting for response" alert in this case.) You can access the responseXML value(serialized string) to check whether the response has come properly or not. Also, if a service has a payload, you can access it as follows:

var payload = services["admin/serviceName"].operations["operationName"].payloadJSON();

To give needed values to the service payload:

var payload = services["admin/serviceName"].operations["operationName"].payloadJSON();                
payload["p:operationName"].payloadAttributeName.$ = value;

The callback function is called with a single parameter - the response from the Web service as a serialized string. This response will be unwrapped and typed just as the return value in the synchronous case. The onError function, returns an error object with the following properties:

  • error.code: when the binding is SOAP, the error.code will be the QName corresponding to the <code> element.
  • error.reason: a human readable error message. When the binding is SOAP, the error.reason corresponds to the <reason> element.
  • error.detail: additional information about the failure, for instance a stack trace. When the binding is SOAP, the error.detail corresponds to the <detail> element.

Authentication

A service requiring username/password authentication can be accessed by setting the username and password annotations on the 'services' global object prior to calling an operation:

  
{services["serviceName"]}.$.usernameSets the username for authenticated calls.
{services["serviceName"]}.$.passwordSets the password for authenticated calls.
              services["helloworld"].$.username = "joey";  
              services["helloworld"].$.password = "fahr451!";  
              var helloWorld = services["admin/helloWorld"].operations["hello"];  
              var payload = null;  
          
  
helloWorld.callback = function(payload) {  
    var responseXML = WSRequest.util._serializeToString(payload);     
      
};  
helloWorld.onError = handleError;  
helloWorld(payload);   
                
function handleError(error) {  
        log (console, "Fault: " + error.reason + "\n\n" + error.detail);  
};  

Using Stubs in Web Pages

In order to use the stub in a Web page, you must import it using a normal script import statement:

<script type="text/javascript" src="?stub"></script>

The stub depends on a WSRequest object to actually make the calls. If you are using IE, you can install WSRequest as an Active-X Object here:http://wso2.org/projects/wsf/ajax/activex. If you are using Firefox, you can install WSRequest as an XPI plugin here: http://dist.wso2.org/products/wsf/ajax/xpi/0.1/. It is often inconvenient to require a download in a web page, so we provide a version written in Javascript using the XMLHTTPRequest object that is native in both IE and Firefox browsers. In order to use the native version, import it as shown below:

<script type="text/javascript" src="js/wso2/WSRequest.js"></script>

When using Firefox, E4X support is built-in. While the normal Javascript stub works fine, returning DOM objects, for a version of the stub that exposes XML results as native E4X XML objects, add the "lang=e4x" parameter to the import:

<script type="text/javascript;e4x=1" src="?stub&lang=e4x"></script>

Using Stubs in Services

To access one Javascript service from another, first make a copy of the stub that you wish to use (for now use "?stub&lang=e4x&localhost=true", so you can get direct access to XML results as native E4X objects), and place it in the {servicename}.resources folder. Then you can include it in the Javascript for your service as follows:

system.include("{location of stub}"); 

File paths in system.include are interpreted relative to the .resources folder for that service. The effect of the include is as if you had copied and pasted the text of the stub directly into the Javascript for the service.

Automatic Type Conversions

When a service returns a value (described by one of the XML Schema built-in types), the stub converts that value into a native Javascript type as follows:

XML Schema type

JavaScript type

xs:anyType

E4X XML object

xs:anyURI

string

xs:base64Binary

string

xs:boolean

boolean

xs:byte

number

xs:date

Date(yyyy, mm, dd, 0:00:00, tz)

xs:dateTime

Date(yyyy, mm, dd, hh:mm:ss, tz)

xs:decimal

string

xs:double

number

xs:duration

Number (in milliseconds)

xs:ENTITIES

Array of strings

xs:ENTITY

string

xs:float

number

xs:gDay

Date(1970, 1, dd, 0:00:00, tz)

xs:gMonth

Date(1970, mm, 1, 0:00:00, tz)

xs:gMonthDay

Date(1970, mm, dd, 0:00:00, tz)

xs:gYear

Date(yyyy, 1, 1, 0:00:00, tz)

xs:gYearMonth

Date (yyyy, mm, 1, 0:00:00, tz)

xs:hexBinary

string

xs:ID

string

xs:IDREF

string

xs:IDREFS

Array of strings

xs:int

number

xs:integer

number

xs:language

string

xs:long

number

xs:Name

string

xs:NCName

string

xs:negativeInteger

number

xs:NMTOKEN

string

xs:NMTOKENS

Array of strings

xs:nonNegativeInteger

number

xs:nonPositiveIntege

r

number

xs:normalizedString

string

xs:NOTATION

string

xs:positiveInteger

number

xs:QName

E4X QName object consisting of name, prefix, local-name, namespace-uri. (Note that DOM stubs don't support QNames yet.)

xs:short

number

xs:string

string

xs:time

Date(1970, 1, 1 hh:mm:ss, tz)

xs:token

string

xs:unsignedByte

number

xs:unsignedInt

number

xs:unsignedLong

number

xs:unsignedShort

number

Utility Functions

The 'services' global object also exposes some utility functions that can be used to further manipulate data types:

String services.utils.toXSdate(date)Convert a Javascript Date to an xs:date format string (discarding the time information.)
String services.utils.toXStime(date)Convert a Javascript Date to an xs:time format string (discarding the date information.)
String services.utils.toXSdateTime(date)Convert a Javascript Date to an xs:dateTime format string.
Date services.utils.parseXSdateTime(date)Convert an xs:date, xs:time, or xs:dateTime format string into a Javascript date.
String services.utils.scheme(url)Extract the scheme from a url.
String services.utils.domain(url)Extract the domain from a url.
String services.utils.domainNoPort(url)Extract the domain from a url, trimming off any port information.
com.atlassian.confluence.content.render.xhtml.migration.exceptions.UnknownMacroMigrationException: The macro 'next_previous_links2' is unknown.