This documentation is for WSO2 Application Server 5.2.0. View documentation for the latest release.
Mashup Service Annotations - Application Server 5.2.0 - WSO2 Documentation
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Current »

In javaScript, functions are objects and any object can have properties dynamically added to it. Properties can be added to the global object, representing the service as a whole, or to individual functions. These predefined properties are referred to as Javascript annotations.

There are two groups of annotations defined in the 'Javascript Service Hosting' feature. Some annotations are used at service-level while the others are used at an operation-level. These annotations help build service or operation meta-data. The documentation annotation is used at both service and operation level.

The following types of annotations are covered in this section:

Documentation Annotation

this.documentation = <div>The <b>simple</b> service has a single operation - <i>echo</i>.</div>;  
echo.documentation = "The echo operation accepts a blob of XML and returns it to the caller unchanged."  
function echo(xmlblob) {  
    return xmlblob;  
}  

The documentation can have either a string value, as shown in the echo.documentation property above, or it can have an XML. The documentation property serves several purposes as follows.

  • It provides a structured documentation within the service. While you can use Javascript comments instead, using a documentation property to describe the service and its operations enables to distinguish between comments describing the implementation, and comments documenting the behavior of the service. It also enables reusing the latter.
  • The documentation for the service (this.documentation) appears in the WSDL as a top-level <documentation> element. The documentation for each operation (echo.documentation in the above example) appears as <documentation> elements on each Interface Operation.
  • The documentation is displayed on the 'TryIt' page. For more information on 'TryIt', refer to section Tools -> TryIt.
  • The documentation is also displayed on the ?doc page. You can access it by concatenating the endpoint URL of the service with the string, ?doc. For example:  http://<your-machine-ip>:<server port>/services/helloworld?doc.

Visible Annotation

By default, each function in a Jjavascript service file is exposed as an operation. But, quite often, functions written for the benefit of other functions should be 'private'; not an entry point that the author wishes to expose to a client. When the 'visible' property of data type Boolean has the value 'false', the function is not exposed as an operation (e.g. it doesn't appear in the WSDL file, TryIt page, etc.)

For example,

function equals(lefthand, righthand){  
     return (lefthand == righthand && typeEquals(lefthand, righthand));  
}  
typeEquals.visible = false;  
function typeEquals(lefthand, righthand){  
     return (typeof(lefthand) == typeof(righthand));  
} 

 In the above example, there is an implicit "equals.visible = true" and an explicit "typeEquals.visible = false."

InputType and OutputType Annotations

 In JavaScript, data types are converted and processed dynamically. As a result, each argument or parameter passed into a function can have any data type, from a string, a number to an object. However, a function typically operates on a specific type of data. For example, if the function processes a calculation, the data passed to it must typically be in (or converted to) a numerical form. This conversion is automatically handled by JavaScript, so even if a function or operation expects a string, you can still pass it a number. However, there are still limits to this capability. For instance, when converting a string to a number, the programmer is expected to specify how the conversion is done using parseInt or parseFloat methods.

 Therefore, despite the dynamic nature of JavaScript's data type handling, function parameters typically have a restricted set of data types that they operate on, and the caller of the function needs to be aware of what types are allowed or preferred by the function. When doing so, the programmer usually relies on examining the source code of the function, its comments or other human-readable documentation.

 Examining a function's source code is not very feasible when it is invoked remotely as a Web service operation. On the other hand, the Web service may be invoked by a language other than javaScript, so it is necessary to define a set of data types that can be used across a variety of programming languages and environments. Therefore, a better option is having machine-readable information about the data type (e.g., ?stub) available to the programmer. You can use the XML Schema Language to define data types in a platform- and language-neutral manner. When you expose a Web service, the types of its data from a simple string or number to a complex structure, can be described in the XML Schema.

 The WSO2 "Javascript Service Hosting" feature facilitates the "inputTypes" annotation to declare the expected data types of an operation as an XML Schema type or as a javaScript type which is eventually converted to an XML Schema type. Although this practice isn't mandatory for the exposure of an operation (we map the parameters into an XML message), having accurate data type information ensures a better user-experience of your functions. The "outputType" annotation serves a similar purpose for the return value.

 "inputTypes" is a javaScript object having a number of properties, equivalent to the number of parameters of the javaScript function. The names of these properties are equal to the names of the function parameters. The value of the respective parameter is the intended data type. In order to declare XML schema types directly, the "xs:" prefix should be used. Else, it is assumed to be a JavaScript (plus E4X) type.

For example,

countWords.inputTypes = {"content":"string", "ignoreHyphens":"boolean"};  
countWords.outputType = "xs:integer";  
function countWords(content, ignoreHyphens) {  
    .....  
}
Interpreting and Handling JavaScript Types

 Although type information can be provided as JavaScript (without the "xs:" prefix) it will eventually be mapped to the XML Schema type system as follows:

Declared JavaScript (plus E4X) Type 
String | string    Treat as xs:string
Number| number    Treat as xs:float
Boolean | booleanTreat as xs:boolean
Date | dateTreat as xs:dateTime
Array | arrayTreat as xs:anyType*
Object | object     Generate XML structure
Xml | XML | xml     Treat as xs:anyType
Xmllist | XMLList | XMLlist | xmlList | xmllist     Treat as xs:anyType*
Any | any     xs:anyType
None | none    No value

 Each of these tokens has alternative case representations, making them pseudo-case-insensitive. In most cases, you can be more precise by using XML schema types, such as "xs:integer" instead of "number" when only integer values are expected. 

 Using Built-in Types of the XML Schema  .inputTypes and .outputType can specify a built-in schema type, indicated by the ?xs:? prefix. When used as an outputType, the return type is serialized as defined below, wrapped in a <return> ?parameter?, and further wrapped in a targetNamespace-qualified RPC wrapper. The schema reflects the use of parameter and rpc wrappers, and uses the declared type as the type of the return parameter.

 When used as an inputType, the parameter value is de-serialized by removing the parameter and rpc wrappers, and converted to a Javascript native type as defined below.

Declared type

Serialization/Desialization
xs:stringstring
xs:normalizedStringstring
xs:tokenstring
xs:languagestring
xs:Namestring
xs:NCNamestring
xs:IDstring
xs:IDREFstring
xs:NMTOKENstring
xs:ENTITYstring
xs:NOTATIONstring
xs:anyURIstring
xs:hexBinarystring
xs:base64Binarystring
xs:floatnumber
xs:doublenumber
xs:durationnumber
xs:integernumber
xs:nonPositiveIntegernumber
xs:negativeIntegernumber
xs:longnumber
xs:intnumber
xs:shortnumber
xs:bytenumber
xs:nonNegativeIntegernumber
xs:unsignedLongnumber
xs:unsignedIntnumber
xs:unsignedShortnumber
xs:unsignedBytenumber
xs:positiveIntegernumber
xs:decimalnumber
xs:booleanboolean
xs:dateTimedate
xs:datedate, time component droped
xs:timedate, date component droped
xs:gYearMonthdate, time and day components droped
xs:gMonthDaydate, time and day components droped
xs:gYeardate, time, month and day components droped
xs:gDaydate, time, year and month components droped
xs:gMonthdate, time, year and day components droped
xs:NMTOKENSstring
xs:IDREFSstring
xs:ENTITIESstring
xs:QNamestring
xs:anyTypeE4X XML object
Support for Optional Parameters and Arrays

To indicate a parameter as optional, an input type token can be followed by a ?. For example:

test.inputTypes = {"required" : "string", "optional1" : "number?", "optional2" : "boolean?"};  
function test(required, optional1, optional2) {  
}

 Messages that omit optional parameters will result in those parameters being assigned the ?undefined? value. Optional parameters will be dispayed as minoccurs="0" in the schema.

 Similarly, an input parameter can be an array. This is modeled in the schema as maxOccurs="unbounded", and repeated elements of the same name are collected into an Array, with items in the array following the normal mappings for that type. The postfix '+' can be added to an input type (except for the 'array' itself) to indicate that maxOccurs='unbounded' should be added.

 The '*' postfix is equivalent to the presence of both '?' and '+', except that zero matching elements in the message results in an empty Array rather than 'undefined'.

Support for Simple, String-Valued Enumerations

 Simple enumerations are common in defining a simple custom type. Additionally, they provide value to the user to have in a description format. For example, enumerations can be converted into a drop down list in the "TryIt" page (refer to Tool -> TryIt).

 A type that is a list of String values separated by '|' is treated as an enumeration.

accountInfo.inputTypes = {"type" : "silver | gold | platinum"};      accountInfo.outputType = "paidup | arrears | unknown";  
function accountInfo(type) {  
     ...  
}
Support for Raw Values In/Out

Without an "outputType" annotation, a return value might be of any of the Javascript types. Therefore, an automatic conversion to XML is done. If the return value is not XML itself, the data in the return value will be returned wrapped in an element called return, which will also have a js:type arribute specifying the JavaScript type returned.

Dynamic type

WrapperValue Serialization
String<return js:type="string">value</return>Value serialized as xs:string
Number<return js:type="number">value</return>Value serialized as xs:float
Boolean<return js:type="boolean">value</return>Value serialized as xs:boolean
Date<return js:type="date">value</return>Value serialized as xs:dateTime
Undefined<return js:type="undefined"></return>No value
Array<return js:type="array">value</return>Value serialized as in Array -> XML
Object<return js:type="object">value</return>Value serialized as in Object -> XML
XML<return js:type="xml">value</return>Serialize directly as XML
XMLList<return js:type="xmllist">value</return>Serialize children directly as XML
Wrapped vs Unwrapped Behavior

All your request and responses are wrapped by default in RPC type wrapper elements by the "Javascript Service Hosting" feature. These wrapper elements provide a conventional structure that can be easily interpreted as function parameters by many different platforms. It provides a means to serialize simple data values such as strings in XML.

Wrapper elements come in handy when using the stub, because the stub generator recognizes this convention and provides the user a uniform interface. If you are interested in preserving the structure and types of your parameters and return values, using the default behaviour is advisable. The wrapper element name of the request will be in the form <operationName> and the wrapper element name of the response will be of the form <operationNameResponse>. The following example illustrates this.

function echoString(param) {  
    return param;  
} 

The wire-level XML input to this function is as follows (with "value" used as the parameter"):

<p:echoString xmlns:p="http://services.mashup.wso2.org/test?xsd">  
    <param>value</param>  
</p:echoString> 

The wire-level XML output from this function is as follows:

<ws:echoStringResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">  
    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="string">value</return>  
</ws:echoStringResponse>

Although providing wrapper elements is the default behavior, the user can always overide this. If you don't want to use wrapper elements, you should set the inputTypes annotation or the outputType annotation (depending on your requirement) to "#raw". This instructs the server to exclude wrapper elements. Note that if you decide to use raw XML, then the input to the function will be the XML value itself. The following example illustrates this use. 

echoString.inputTypes="#raw";  
echoString.outputType="#raw";  
function echoString(param) {  
    return param;  
}  

The wire-level XML input to this function is as follows (with "<value/>" used as the parameter"):

<value/>

 The wire-level XML output from this function is as follows:

<value/>

As you can see that "#raw" forces the mashup server to exclude the wrapper elements.

Array -> XML Conversion 

When arrays are returned from an operation, they are serialized to XML as shown in the following example:

returnArrayFunction.outputType="array";  
function returnArrayFunction() {  
    var returnArray = new Array();  
    returnArray.property1 = "value1";  
    returnArray.property2 = 2.270;  
    returnArray.property3 = <value>2</value>;  
    return returnArray;  
} 

 The output of the function is serialized as follows:

<ws:returnArrayFunctionResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">  
    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="array">  
        <property3 js:type="xml">  
            <value>2</value>  
        </property3>  
        <property1 js:type="string">value1</property1>  
        <property2 js:type="number">2.27</property2>  
    </return>  
</ws:returnArrayFunctionResponse> 

 As arrays can contain multiple elements, they are always wrapped in a return element with the js:type attribute set to indicate that the return is an array. Alternatively, an array can be declared as follows (Note that the array properties are declared using indices):

returnArrayFunction.outputType="array";  
function returnArrayFunction(){  
    var returnArray = new Array();  
    returnArray[0] = "value1";  
    returnArray[1] = 2.270;  
    returnArray[2] = <value>2</value>;  
    return returnArray;  
}

 In this case the response will look as follows. Instead of having the elements as properties, the elements will be as items as they have no name.

<ws:returnArrayFunctionResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">  
    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="array">  
        <item js:type="string">value1</item>  
        <item js:type="number">2.27</item>  
        <item js:type="xml">  
            <value>2</value>  
        </item>  
    </return>  
</ws:returnArrayFunctionResponse>
Object -> XML Conversion

Serialization of objects is identical to that of Arrays with the exception being the use of type 'js:object', as shown in the following example:

returnObjectFunction.outputType="object";  
function returnObjectFunction() {  
    var returnObject = new objectFunc();  
    returnObject.property1 = "value1";  
    returnObject.property2 = 2.270;  
    returnObject.property3 = <value>2</value>;  
    return returnObject;  
}  
  
objectFunc.visible=false;  
function objectFunc(){  
} 

 The response is as follows:

<ws:returnObjectFunctionResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">  
    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="object">  
        <property3 js:type="xml">  
            <value>2</value>  
        </property3>  
        <property1 js:type="string">value1</property1>  
        <property2 js:type="number">2.27</property2>  
    </return>  
</ws:returnObjectFunctionResponse>  

RESTful Service Annotations

The "httpMethod", "httpLocation" and the "ignoreUncited" annotations help you to write RESTful services with relative ease.

httpMethod Annotation

Helps you specify the HTTP Method under which the operation will be exposed. Supported HTTP methods are GET, POST, PUT and DELETE.

exposeViaGET.httpMethod="GET";  
function exposeViaGET() {  
    // get something  
}  
  
exposeViaPOST.httpMethod="POST";  
function exposeViaPOST() {  
    // post something  
}  
  
exposeViaPUT.httpMethod="PUT";  
function exposeViaPUT() {  
    // put something  
}  
  
exposeViaDELETE.httpMethod="DELETE";  
function exposeViaDELETE() {  
    // delete something  
}

If the httpMethod is not specified, the operation is exposed with a default HTTP method. The defaulting rule is as follows,

1. If the operation is marked as "safe" using the {function}.safe annotation, the HTTP method defaults to GET (the .safe annotation defaults to false unless explicitly set).

defaultsToGET.safe = true;  
function defaultsToGET() {  
    // returns something  
}  

2. If the operation is not marked as safe, or marked as 'not safe' the HTTP method defaults to POST.

function defaultsToPOST() {  
    // do something  
} 
httpLocation Annotation

The httpLocation annotation helps take control of the URI and expose this operation under a logical URI by specifying a pattern or a template for serializing input parameters of the function in the request URI. Curly braces are used to specify the name of an input parameter, which determines where the instance data of that parameter appears in the 'path' component of the request URI. In the stubs, the curly brace-enclosed name is replaced with instance data in constructing the path component.

The remaining input instance data (not specified by the httpLocation annotation) is serialized into the query string portion of the URI. In order for the httpLocation annotation to be effective, you have to use it in conjuction with inputTypes annotation. The server needs to be aware of how to build the payload from the request URI and the inputTypes annotation makes this possible.

The following example shows the use of httpMethod and httpLocation annotations.

getWeather.safe = true;  
getWeather.httpMethod = "GET";  
getWeather.httpLocation = "weather/{city}";  
getWeather.inputTypes = "string";  
function getWeather(city) {  
    var details = session.get(city);  
    // return whether details of city  
    // for e.g to get the weather details of colombo you need to perform a  
    // GET on http://localhost:7762/services/samples/RESTSample/weather/colombo  
}  
  
POSTWeather.httpMethod = "POST";  
POSTWeather.httpLocation = "weather/{city}";  
POSTWeather.inputTypes = {"city" : "string", "weatherDetails" : "string"};  
function POSTWeather(city, weatherDetails) {  
    // Add the weather details of city  
    // For e.g to add weather details of colombo you need to perform a POST  
    // on http://localhost:7762/services/samples/RESTSample/weather/colombo  
    // with the following payload  
    // <POSTWeather>  
    //      <city>colombo</city>  
    //      <weatherDetails>30</weatherDetails>  
    // </POSTWeather>  
}  
  
DeleteWeather.httpMethod = "DELETE";  
DeleteWeather.httpLocation = "weather/{city}";  
DeleteWeather.inputTypes = "string";  
function DeleteWeather(city) {  
    // Delete the weather details of city  
    // For e.g to delete the weather details of colombo you need to perform  
    // a DELETE on http://localhost:7762/services/samples/RESTSample/weather/colombo  
}  
  
PUTWeather.httpMethod = "PUT";  
PUTWeather.httpLocation = "weather/{city}";  
PUTWeather.inputTypes = {"city" : "string", "weatherDetails" : "string"};  
function PUTWeather(city, weatherDetails){  
    // Update the weather details of city  
    // For e.g to update the weather details of colombo you need to perform  
    // a PUT on http://localhost:7762/services/samples/RESTSample/weather/colombo with  
    // the following payload  
    // <PUTWeather>  
    //      <city>colombo</city>  
    //      <weatherDetails>35</weatherDetails>  
    // </PUTWeather>  
} 
ignoreUncited Annotation

This boolean indicates whether elements not cited in the httpLocation property MUST be appended to the request URI or not. If the value of this property is "false", elements not cited in httpLocation will be appended to the request URI. Otherwise, those are NOT serialized in the request URI.

Annotations for Life Cycle Support

Service Lifecycle support helps manage a particular service deployed in a Carbon instance and is powered by the "init" and "destroy" service-level annotations. The function referred to by the "init" annotation will be executed when the particullar service is been deployed while the function referred to by the "destroy" annotation will be executed when it is undeployed.

 In addition to the "init" and "destroy" annotations, which control what happens when a service is deployed or undeployed, another useful annotation is "undispatched". Under normal circumstances, when the Server receives a request, it first dispatches the request to the correct service and then to the corresponding operation depending on the information available in the request. However, there are cases where the server is unable to find the correct operation from the information available in the request. In such cases, an error is thrown. Users can overcome the above issue by using the "undispatched" annotation. It routes all undispatched operations that come to a particular service, to this special operation.

 For information, refer to the examples described below.

The init or destroy Annotations

The "init" or "destroy" function can be specified in three different ways. They are as follows:

1) Using this annotation in the following manner, the function you reference can be reusable within your service and make the referenced init function appear in the WSDL. For example, if you deploy the following mashup, the corresponding WSDL will have an operation called myInitFunction.

//Because the function referenced by init is not a annonymous fucntion it can be reused in a mashup  
this.init=myInitFunction;  
function myInitFunction() {  
    // Do some stuff upon deployment  
    system.log("init");  
} 

2) Using this annotation in the following manner, you cannot reuse the function referenced by the annotation. Still, using it in this manner will make the referenced init function appear in the WSDL. For example if you deploy the following mashup, the corresponding WSDL will have a operation calledmyInitFunction.

//The function referenced by init cannot be reused in your mashup  
this.init = function myInitFunction() {  
    // Do some stuff upon deployment  
    system.log("init");  
} 

3) Using this annotation in the following manner, you cannot reuse the function referenced by the annotation. Furthermore, the referenced init function will not appear in the WSDL.

//The function referenced by init cannot be reused in your mashup  
this.init = function () {  
    // Do some stuff upon deployment  
    system.log("init");  
}
init Annotation

The init annotation helps you specify the function that should be executed when a service is deployed. Note that using the session host object from within a init function will not preserve your session. The reason is that the init function is called within the server and sessions are created when requests enter the server.

destroy Annotation

The destroy annotation helps you specify the function that should be executed when a service is undeployed. Note that using the session host object from within a destroy function will not give you the service's session. The reason is that the destroy function is called within the server and sessions are created when requests enter the server.

undispatched Annotation

All undispatched operations that came into a particullar service will be routed to the operation refereed to by the undispatched annotation. As the server should be able to call this function from the requests it receives, using this annotation in the third variant defined above is not allowed. The operation referred to be the undispatched annotation should be available in the WSDL.

this.undispatched = function undispatchedOperation() {  
    // Do something here  
    system.log("undispatched");  
}

Miscellaneous Annotations  

For advanced usage, there are a number of other properties that can be used:

  • this.targetNamespace: places the components of the description (WSDL 2.0, WSDL 1.1) in a specific namespace. By default, the targetNamespace is http://services.mashup.wso2.org/<service name>, which is not guaranteed to be unique.
  • this.schemaTargetNamespace : sets the schema target namespace, which will be used as the namespace for any wrapper elements generated by the server. By default the targetnamespace is http://services.mashup.wso2.org/<servicename>?xsd.
  • this.serviceName : exposes the service under an alternate name (in the WSDL, TryIt, documentation, etc.) By default the serviceName is the name of the JavaScript file (minus the '.js'.)
  • {function}.safe :indicates whether repeated calls to the operation cause side effects. For example, two calls to a 'bill me' operation would normally result in two charges, while two calls to a 'current activity' operation would not cause any harmful side-effects. The safe annotation maps to the wsdlx:safe attribute, and helps determine whether GET or POST is the most appropriate HTTP method to use.
  • {function}.operationName : exposes the function as an operation under an alternate name (in the WSDL, TryIt, documentation, etc.). By default, the operationName is the name of the function.

 

  • No labels