Alfresco Documentation
Published on Alfresco Documentation (https://docs.alfresco.com)

Home > Alfresco Community Edition 201911 GA > Developer guide > Platform extensions > Platform extension points > Web scripts

Web scripts

Repository web scripts are the fundamental building blocks used for extending the REST API in Alfresco Community Edition.
Information Repository web scripts
Support Status Full Support [1]
Architecture Information
  • Platform Architecture [2]
  • Presentation Web Scripts (Surf) vs Data Web Scripts (Repository) [3]
Description Web Scripts are a way of implementing REST-based API [4]. They could also be referred to as Web Services. They are stateless and scale extremely well. Repository Web Scripts are defined in XML, JavaScript, and FreeMarker files and stored under alfresco/extension/templates/webscripts. Repository Web Scripts are referred to as Data Web Scripts as they usually return JSON or XML.

Before embarking on implementing a Repository web scripts it is recommended that you establish if the required functionality is already available out-of-the-box. Many operations that you might want to perform may be available, see Alfresco REST API [5].

The simplest Web Script you can write consists of a descriptor and a template. The descriptor will tell you what URL that should be used to invoke the Web Script. The template is used to assemble the output returned from the Web Script. This kind of Web Script is very static to its nature and will always return the exact same content. Most Web Scripts also include a controller that is used to dynamically assemble a map of data that is then processed by the template to produce the final output. The data that the controller produces could come from anywhere as the controller can be implemented in both JavaScript and Java. Any content from the repository that should be included in the response can be fetched via Alfresco Community Edition-specific JavaScript root objects, such as companyhome, or services, such as the Node Service, if the controller is implemented in Java.

The following picture illustrates how a Repository Web Script request is processed:

The controller can fetch content from different sources, such as the repository, or a remote Web Service on the Internet. Note that the special root object called remote [6], which is available for Surf web scripts to fetch remote data on the internet, is not available when implementing a Repository Web Script JavaScript controller. To fetch remote data on the Internet from a Repository Web Script, a Java controller is needed.

Now, to get going implementing web scripts we will start with the simplest possible Repository Web Script. The usual Hello World example comes to mind. When implementing a new Web Script it is good to start with the descriptor file, it will define what URL(s) that should be used to invoke the Web Script. It is defined in XML and looks something like this:

<webscript>
    <shortname>Hello World</shortname>
    <description>Hello World Sample Web Script that responds back with a greeting</description>
    <url>/tutorial/helloworld</url>
    <format default="html"></format>
    <family>Alfresco Tutorials</family>
</webscript>

The important part here is the <url> element, which determines what URL should be used to invoke the Web Script. When specifying the URL leave out the part that maps to the Web Script dispatcher Servlet, which is http://{host}:{port}/alfresco/service. So to invoke this Web Script use a URL with the http://{host}:{port}/alfresco/service/tutorial/helloworld format.

Next important thing in the descriptor file is the <format element, which specifies what content format we can expect in the response when invoking this Web Script. In this case it will return a HTML fragment, so we set format to default="html". Finally we need to somehow define a unique identifier for this Web Script, which will be used to look up other files that are part of the Web Script implementation. This is handled implicitly by the file name convention, which for Web Script descriptor files follow the <web script id>.<http method>.desc.xml format. If we store this descriptor in a file called helloworld.get.desc.xml then the unique identifier will be helloworld. But that's not all, the HTTP method also plays a part in the identification of a Web Script, in this case it is set to get, which means it is intended to be invoked with a HTTP GET Request.

Important: The Web Script URL needs to be unique throughout the Alfresco Community Edition installation. And if two or more web scripts have the same identifier, then they need to be stored in different directory locations. For example, if you have two extensions deploying a Web Script with the same file name, in the same location (i.e. directory), then the last one to be deployed will overwrite the other one, even if the URL is different between the two.

To complete the Hello World Web Script implementation we just need a template to go along with the descriptor, it is defined in a FreeMarker file and looks like this:

<h2>Hello World!</h2>

Web Script template file names also follow a naming convention: <web script id>.<http method>.<format>.ftl. The above template could be stored in a file called helloworld.get.html.ftl, which would implicitly associate it with the descriptor file as it has the same identifier and HTTP method. We are also indicating that this template produces HTML markup. This Web Script implementation is now complete.

To try out the Hello World Web Script we first need to deploy it by copying the files to the correct directory in the Alfresco Community Edition installation, see below for locations. Then refresh the web scripts from the http://{host}:{port}/alfresco/service/index page so Alfresco Community Edition knows about it. And then invoke it using the URL in a browser as follows:

Most of the time the content that is returned is provided indirectly via a controller. The controller sets up the model containing the data that should be passed over to the template. Controllers can be implemented in both JavaScript (this is server side JavaScript, Alfresco Community Edition provides this by embedding the Rhino JavaScript engine) and Java. Let's add a JavaScript controller for the Hello World Web Script. It will put a property called message in the model. This new property will contain a slightly improved Hello World message that includes the name of the logged in user. Here is the controller implementation:

model.message = "Hello World " + person.properties.firstName + ' ' + person.properties.lastName + "!";

Here we use an Alfresco Community Edition-specific JavaScript root object called person to get first and last name of the logged in user. The model variable is automatically available to us in the controller and we can put whatever data we want in it for later use in the template.

The Web Script controller file names follow the <web script id>.<http method>.js naming convention. The above controller should be stored in a file called helloworld.get.js so it is matched up with the Hello World Web Script descriptor. To take advantage of this new data in the model we need to update the template as follows:

<h2>${message}</h2>

The update to the Web Script is now finished. However, if we were to try and invoke the Web Script we would see an exception as currently it is not set up to authenticate with a username and password. We cannot use the people root object to access Repository information about users without being authenticated. In fact, we cannot access anything in the Repository without first authenticating, so using other root objects such as companyhome requires authentication too.

Authentication is configured in the descriptor file with an extra <authentication> element as follows:

<webscript>
    <shortname>Hello World</shortname>
    <description>Hello World Sample Web Script that responds back with a greeting</description>
    <url>/tutorial/helloworld</url>
    <format default="html"></format>
    <authentication>user</authentication>
    <family>Alfresco Tutorials</family>
</webscript>

When setting the authentication property to be able to read and write to the Repository we need to have these operations wrapped in a transaction. This is automatically done as soon as we set the authentication element to anything else than none. By default another element called <transaction> is then set to required.

After deploying the updated Web Script files and the new controller file, and refreshing the web scripts, we will see the following when invoking it again (assuming we logged in as Administrator):

Now, what if we wanted to present the Hello World message in different languages depending on what the browser Accept-Language header was, how would we do that?

We would then turn to Web Script i18n properties files. These files are created in the same way as Java resource bundles. The naming convention for these files is <web script id>.<http method>[_<locale>].properties. For the default English resource file you can leave out the locale. So for our Hello World Web Script it would be called helloworld.get.properties and contain the following:

hello.world=Hello World

To add a Swedish translation we would create a properties file called helloworld.get_sv.properties with the following content:

hello.world=Hej Världen

To make use of this property we would have to update the controller as follows:

model.message = person.properties.firstName + ' ' + person.properties.lastName + "!";

Leaving out the Hello World string so it can be localized. The template need the following update to read the resource string:

<h2>${msg("hello.world")} - ${personName}</h2>

There are also situations where we just want to be able to externally configure the Web Script with minimal changes to the main implementation of it. Basically we don't want to touch the descriptor, controller, or template. Just feed it with some new configuration. Let's say for example that our greeting message should be slightly different at certain times of the year, such as an extra Merry Christmas message around that time.

This can be done with an extra configuration file that follows the <web script id>.<http method>.config.xml naming convention. The Hello World Web Script configuration will look like this:

<greeting>
    <text>Merry Christmas!</text>
    <active>true</active>
</greeting>

The configuration file can contain any arbitrary XML structure. In this case it contains a message text and an indication if this text should be active or not. We store this configuration in a file called helloworld.get.config.xml. To access this configuration we would have to make a change to the controller as follows:

var greeting = new XML(config.script);
model.greetingActive = greeting.active[0].toString();
model.greetingText = greeting.text[0].toString();
model.personName = person.properties.firstName + ' ' + person.properties.lastName + "!";

We use the JavaScript root object config to access the XML. This is then fed into the XML object, which is part of the E4X JavaScript library that enables us to process XML directly in JavaScript (more info: http://www.w3schools.com/e4x/default.asp). We can then navigate into the XML structure and grab the data that we need. We add two variables to the model to hold the greeting message and if it should be active or not. All we got to do now is update the template to take advantage of the new data:

<h2>${msg("hello.world")} - ${personName}</h2>
<#if greetingActive == "true">
    <p>
        <i>${greetingText}</i>
    </p>
</#if>

This is the first time we have started to use some FreeMarker directives. Common statements such as if,then,else are supported. Directives are preceded with #. Note that when you use model variables such as the greetingActive inside a directive statement they don't have to be enclosed in ${ }.

Invoking the Hello World Web Script should now give us the following result:

It is now very simple to change the extra message to whatever we want without having to touch the main implementation of the Web Script, just update the helloworld.get.config.xml file, and we can turn off the message all together if we want to.

Sometimes when implementing a Web Script there are things that cannot be done in a JavaScript controller, such as accessing the file system and fetching content on the Internet. We then need to turn to Java based controllers. To implement a Web Script controller in Java we create a class that extends the org.springframework.extensions.webscripts.DeclarativeWebScript class. Using a Java controller will allow us to fetch and process data from wherever we want to.

Let's implement a Java controller that just adds a current date and time variable to the model:

package org.alfresco.tutorial.webscripts;

import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptRequest;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class HelloWorldWebScript extends DeclarativeWebScript {
    @Override
    protected Map<String, Object> executeImpl(
            WebScriptRequest req, Status status, Cache cache) {
        Map<String, Object> model = new HashMap<String, Object>();
        model.put("currentDateTime", new Date());
        return model;
    }
}

Note here that we are expected to return a model object, which is just a hash map. When we got both a JavaScript controller and a Java controller the latter one is executed first. The new Java controller is not yet associated with the Hello World Web Script. We need to define a Spring bean for it with an id that connects the controller with this Web Script:

<beans>
	<bean id="webscript.alfresco.tutorials.helloworld.get"
		  class="org.alfresco.tutorial.webscripts.HelloWorldWebScript"
		  parent="webscript">
	</bean>
</beans>

The id should be specified following the webscript.<packageId>.<web-script-id>.<httpMethod> format. The trickiest part of the id is probably the packageId. When specified as in the above example it is assumed that the descriptor file is located in the alfresco/extension/templates/webscripts/alfresco/tutorials directory.

With the new currentDateTime variable in the model we can use it in the template to get it displayed in the response:

<#assign datetimeformat="yyyy-MM-dd HH:mm:ss">
<h2>${msg("hello.world")} - ${personName}</h2>
<#if greetingActive == "true">
    <p>
        <i>${greetingText}</i>
    </p>
</#if>
<p>The time is now: "${currentDateTime?string(datetimeformat)}</p> 

Here we use another FreeMarker directive called assign that can be used to define new variables. In this case we define a new variable datetimeformat to hold the date and time format we want to use when displaying current date and time. To display the date in this format we use a so called built-in for dates called string. Calling the Web Script will now show the following response:

Important: The DeclarativeWebScript class is used when we have a template, and maybe a JavaScript controller as part of the Web Script. But there are situations, such as streaming and downloading a file, where there is no need for a template. In these cases we can extend the org.springframework.extensions.webscripts.AbstractWebScript class instead. It has an execute method that will allow you to return nothing and instead just put something on the response output stream, as in the following example:
package org.alfresco.tutorial.webscripts;

import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;

import java.io.IOException;

public class JSONResponseWebScript extends AbstractWebScript {
    @Override
    public void execute(WebScriptRequest req, WebScriptResponse res)
            throws IOException {
        try {
            JSONObject obj = new JSONObject();
            obj.put("name", "Alfresco");
            String jsonString = obj.toString();
            res.getWriter().write(jsonString);
        } catch (JSONException e) {
            throw new WebScriptException("Unable to serialize JSON");
        }
    }
}

The Hello World Web Script demonstrates most of the features available to us when implementing web scripts. However, it might not be the most realistic Web Script implementation, it is not something we would need to do in a "real" project. It is more likely that we will have to implement a REST API based on a custom content model, such as the ACME sample content model [7].

The key principles of REST involve separating your API into logical resources. These resources are manipulated using HTTP requests where the method (GET, POST, PUT, DELETE) has specific meaning.

When working with custom content models, what can we make a resource? Normally, these should be nouns that make sense from the perspective of the API consumer. We should not have internal implementation details visible in our API! When looking at a content model it probably makes sense to use the types as resources, so for the ACME content model we could have the ACME Document, ACME Contract, and so on as resources.

When we have identified our resources, we need to identify what actions apply to them and how those would map to the API. REST-ful principles provide strategies to handle CRUD actions using HTTP methods mapped as follows:

  • GET /acmedocs - Retrieves a list of ACME Documents
  • GET /acmedocs/{noderef} - Retrieves a specific ACME Document with specified node reference
  • POST /acmedocs - Creates a new ACME Document
  • PUT /acmedocs/{noderef} - Updates ACME Document with specified node reference
  • DELETE /acmedocs/{noderef} - Deletes ACME Document with specified node reference

A good thing about REST is that we leverage existing HTTP methods to implement significant functionality on just a single /acmedocs endpoint. There are no method naming conventions to follow and the URL structure is clean and clear.

Try and keep the resource URLs as lean as possible. Things like filters, sorting, search, and what properties to return can quite easily be implemented as parameters on top of the base URL.

Here are some examples:

  • Filtering: GET /acmedocs?sc={security classification} - Retrieves a list of ACME Documents that has been tagged with passed in security classification.
  • Sorting: GET /acmedocs?sort=[-+]{property} - Retrieves a list of ACME Documents sorted in ascending or descending order on the property passed in.
  • Searching: GET /acmedocs?q={keyword} - Retrieves a list of ACME Documents matching FTS on keyword passed in.
  • Properties: GET /acmedocs?props={field1,field2,...} - Retrieves a list of ACME Documents, only the specified properties are returned.
  • GET /acmedocs?q=London&sc=Public&sort=-cm:created - Combination of the above.

When it comes to response format JSON is usually a good choice as it is compact and works well with most programming languages and widget libraries.

As a demonstration on how to implement a REST API according to these best practices, we will look at how to implement a Web Script that can be used to return a list of ACME documents matching a keyword using Full Text Search (FTS). Based on REST API design principles, the descriptor would then look something like this:

<webscript>
    <shortname>Search ACME Documents</shortname>
    <description>Returns metadata as JSON for all ACME documents in the repository that matches search keyword</description>
    <url>/tutorial/acmedocs?q={keyword}</url>
    <authentication>user</authentication>
    <format default="json"></format>
    <family>Alfresco Tutorials</family>
</webscript>

The above descriptor could be stored in a file called acme-documents.get.desc.xml as this Web Script should be used to search for files with the ACME document type applied. To invoke this Web Script we would use a URL with the format http://{host}:{port}/alfresco/service/tutorial/acmedocs?q=london.

Next step is to create a controller that takes the search keyword, does a FTS, and then adds information about the matching nodes to the model object:

function AcmeDocumentInfo(doc) {
    this.name = doc.name;
    this.creator = doc.properties.creator;
    this.createdDate = doc.properties.created;
    this.modifier = doc.properties.modifier;
    this.modifiedDate = doc.properties.modified;
    this.docId = doc.properties["acme:documentId"];
    this.securityClassification = doc.properties["acme:securityClassification"];
}

function main() {
    var searchKeyword = args["q"];
    if (searchKeyword == null || searchKeyword.length == 0) {
        searchKeyword = "";
    } else {
        searchKeyword = " AND TEXT:\"" + searchKeyword + "\"";
    }

    var acmeDocNodes = search.luceneSearch("TYPE:\"acme:document\"" + searchKeyword);
    if (acmeDocNodes == null || acmeDocNodes.length == 0) {
        status.code = 404;
        status.message = "No ACME documents found";
        status.redirect = true;
    } else {
        var acmeDocInfos = new Array();
        for (i = 0; i < acmeDocNodes.length; i++) {
            acmeDocInfos[i] = new AcmeDocumentInfo(acmeDocNodes[i]);
        }
        model.acmeDocs = acmeDocInfos;
        return model;
    }
}

main();

Here we first check if we got a search keyword passed in, if we don't we will exclude the FTS from the query. We then do the Lucene search on the ACME Document type and keyword using the Alfresco Community Edition-specific search root object. If we get any nodes back we create an array of information objects that we add to the model to be sent to the template. If the query did not match any nodes we use the special status root object to send back a HTTP 404 not found message.

The controller needs to be stored in a file called acme-documents.get.js to match up with the descriptor.

The template for this Web Script should construct a JSON representation of the resources/nodes that match the query:

<#assign datetimeformat="EEE, dd MMM yyyy HH:mm:ss zzz">
{
    "acmeDocs" : [
        <#list acmeDocs as acmeDoc>
            {
                "name"          : "${acmeDoc.name}",
                "creator"       : "${acmeDoc.creator}",
                "createdDate"   : "${acmeDoc.createdDate?string(datetimeformat)}",
                "modifier"      : "${acmeDoc.modifier}",
                "modifiedDate"  : "${acmeDoc.modifiedDate?string(datetimeformat)}",
                "docId"         : "${acmeDoc.docId!"Unknown"}",
                "securityClass" : "${acmeDoc.securityClassification!"Unknown"}"
            }
            <#if acmeDoc_has_next>,</#if>
        </#list>
    ]
}

Here a new FreeMarker directive called list is used to loop through the document information for the matching nodes. We also use a very handy build-in (!) that will check if the variable has a value (i.e. is not null), if it doesn't the right hand side value will be used as default.

The template should be stored in a file called acme-documents.get.json.ftl as it returns JSON and should be matched up with the correct descriptor.

This completes this ACME Docs Web Script, executing it will return a result looking something like this:

In this call there were two documents that matched, having the ACME Document type applied, or a sub-type such as ACME Contract, and with a text that contained the word "sample".
Important: The above example with free text search will actually match both sample, sampling, and sampled. The search engine uses stemming so all these variations will be reduced to their word stem [8], base or root [9] form before matching starts.

We have now seen a lot of examples of how to get stuff from the repository, what about if we wanted to POST some stuff to the repository and store it? This is simple, tell the web script container that the web script is of type POST, and that we expect to upload and store stuff in the repository with it.

As an example, let's create an ACME Docs web script that can be used to upload some JSON data with information that is to be used when creating an ACME Text document. The descriptor will look like this:

<webscript>
    <shortname>Create ACME Document</shortname>
    <description>Create an ACME Text Document by uploading JSON data
        with both metadata and content for the text document.

        POST body should include JSON such as:
        {
        name: "acmedocument2.txt",
        docId: "DOC002",
        securityClass: "Public",
        content: "Some text to represent the content of the document"
        }
    </description>
    <url>/tutorial/acmedocs</url>
    <authentication>user</authentication>
    <transaction>required</transaction>
    <format default="html">any</format>
    <family>Alfresco Tutorials</family>
</webscript>

The above descriptor could be stored in a file called acme-documents.post.desc.xml as this Web Script should be used to POST stuff to the Repository. To invoke this Web Script we would use a cURL command looking something like this:

curl -v -u admin:admin -d @sample.json -H 'Content-Type:application/json' http://localhost:8080/alfresco/service/tutorial/acmedocs

The sample.json file would contain the JSON structure as described in the descriptor. Next up is the controller, which should extract the JSON and then create the ACME Text Document based on the data:

// Get the POSTed JSON data
var name = json.get("name");
var docId = json.get("docId");
var securityClass = json.get("securityClass");
var content = json.get("content");

// Create the new ACME Text Document
var acmeTextDocFileName = name;
var guestHomeFolder = companyhome.childByNamePath("Guest Home");
var acmeTextDocFile = guestHomeFolder.childByNamePath(acmeTextDocFileName);
if (acmeTextDocFile == null) {
    var contentType = "acme:document";
    var properties = new Array();
    properties['acme:documentId'] = docId;
    properties['acme:securityClassification'] = securityClass;
    acmeTextDocFile = guestHomeFolder.createNode(acmeTextDocFileName, contentType, properties);
    acmeTextDocFile.content = content;
    acmeTextDocFile.mimetype = "text/plain";

    // Send back the NodeRef so it can be further used if necessary
    model.nodeRef = acmeTextDocFile.nodeRef;
} else {
    status.code = 404;
    status.message = "ACME Text Document with name: '" + acmeTextDocFileName + "' already exist!";
    status.redirect = true;
}

The controller file should be called acme-documents.post.json.js to tell the Web Script container that it will be receiving POSTed JSON. When the controller is expecting JSON like this it provides a convenience root object called json that can be used to extract the JSON data. We then use another Alfresco Community Edition-specific root object called companyhome that can be used to search for a folder, such as /Guest Home in this case. The childByNamePath assumes that you are searching from /Company Home so no need to specify it in the path to the node, this method can also be used to search for files. The node reference for the newly created ACME Text document is passed in to the template via the model.

The template for the Web Script is simple and looks like this:

<p>The ACME Document was added successfully with the node reference: ${nodeRef}</p>

The template file should be called acme-documents.post.html.ftl to be associated with the ACME Documents Web Script.

Deployment - App Server
  • tomcat/shared/classes/alfresco/extension/templates/webscripts/{domain specific directory path} - Descriptor, JavaScript controller, template, properties files, configurations (Untouched by re-deployments and upgrades)
  • Note. if you are developing a Web Script with a Java controller you are better off using a proper SDK project, see next.
Deployment All-in-One SDK project [10].
  • aio/platform-jar/src/main/resources/alfresco/extension/templates/webscripts/{domain specific directory path} - Descriptor, JavaScript controller, template, properties files, configurations
  • aio/platform-jar/src/main/java/{domain specific directory path} - implementation of Java controller
  • aio/platform-jar/src/main/resources/alfresco/module/platform-jar/context/webscript-context.xml - Java controller Spring Bean
More Information
  • Web Script naming conventions [11]
  • JavaScript root objects [12] - for use in a JavaScript controller
  • FreeMarker root objects [13] - for use in a template
  • Where to put your web scripts [14] (When trying them out without a build project)
  • Caching approach [15] - HTTP Response caching and web scripts
  • Presentation Tier web scripts [16] - i.e. Surf web scripts
  • Web Script examples that create Data Lists [17]
Sample Code
  • Different Web Script implementations as in above description [18]
  • web scripts that create Data Lists [19]
Tutorials
  • Jeff Potts web scripts tutorial [20] - a must read
  • XML Configuration [21] - Additional XML configuration for Web Script
  • Cache Control [22] - Additional Cache control configuration for Web Script
  • POST data processing [23] - Additional Cache control configuration for Web Script
  • Repository-tier web scripts [24] Web scripts provide a unique way to programmatically interact with the Alfresco Community Edition server. Unlike other interfaces exposed by Alfresco Community Edition, web scripts offer a RESTful API for the content residing in the repository. The REST (Representational State Transfer) web architecture is based on HTTP requests and responses, URIs (Uniform Resource Identifiers), and document types.
Parent topic: Platform extension points [25]

Repository-tier web scripts

Web scripts provide a unique way to programmatically interact with the Alfresco Community Edition server. Unlike other interfaces exposed by Alfresco Community Edition, web scripts offer a RESTful API for the content residing in the repository. The REST (Representational State Transfer) web architecture is based on HTTP requests and responses, URIs (Uniform Resource Identifiers), and document types.

Web scripts let you implement your own RESTful API without tooling or Java knowledge, requiring only a text editor. This approach to developing an Alfresco Community Edition API means that web scripts offer many advantages over existing technologies, including ease and speed of development, and flexibility in API design. By focusing on the RESTful architectural style, web scripts let you build custom URI-identified and HTTP accessible content management web services backed by the Alfresco Community Edition server.

Web scripts provide RESTful access to content held within your repository. You can place controls on your content to manage it and provide uniform access for a wide variety of client applications and services, such as a browser, portal, search engine, or custom application. Because of the inherent distributed nature of this interface, all repositories within the enterprise can resemble one logical collection of inter-related documents (like the web), letting you apply web technologies such as caching, authentication, proxies, and negotiation to your repository resources.

You can build your own RESTful interface using lightweight scripting technologies (such as JavaScript and FreeMarker), allowing you to arbitrarily map any content in the repository to resources on the web, or you can use out-of-the-box web scripts that already encapsulate many of the mappings. The Alfresco Community Edition CMIS (Content Management Interoperability Services) AtomPub binding is implemented as a series of web scripts.

You can use web scripts for various solutions, such as:

  • Integrating Alfresco Community Edition with third party systems
  • Providing feeds
  • Developing data services
  • Developing UI services such as portlets
  • Customizing search
  • Acting as a back-end to client tools, such as Orbeon Forms
  • Integrating with Microsoft Office
  • Developing Facebook applications
  • Building UI components in Surf
  • Understanding web scripts [26] Web scripts let you programmatically interact with the Alfresco Community Edition server. Unlike other interfaces exposed by Alfresco Community Edition, web scripts offer a RESTful API for content in the repository.
  • Web script types [3] A web script is a service bound to a URI that responds to HTTP methods such as GET, POST, PUT, and DELETE.
  • Web Script Framework [27] The Web Script Framework is designed according to the Model View Controller (MVC) pattern (sometimes referred to as MVC for the web). While its primary design goal is to ensure that simple web scripts are easy to develop, advanced web scripts can support various features, such as rendering outputs in multiple languages, exposing and adhering to configuration options, and handling HTML form uploads.
  • Invoking web scripts [28] A common client for invoking web scripts is a web browser, as many content rich applications are web applications. The web browser also provides an easy and convenient client for testing web scripts while developing them.
  • Working with client limitations [29] Not all HTTP clients are equivalent in their capabilities. Many clients have limitations that mean certain HTTP features are not supported. Rather than dismiss those clients and reduce the scope of where web scripts can be invoked, the Web Script Framework provides helpers for working around those limitations.
  • Exception handling in web scripts [30] Great care must be taken when using exception handling within a web script. If any exception expected to be handled by the repository is handled by the web script, this can lead to inconsistency of state, and potentially corruption of the repository.
  • Caching [15] The Web Script Framework complies with HTTP caching, in particular with the notions of Last Modified Time and ETag (a kind of hash), allowing the caching of Web script responses using HTTP-aware caches.
  • Authenticating web scripts [31] You can invoke a web script without first authenticating, that is, without specifying a user name and password as identification. This is rare when interacting with the Alfresco Community Edition server as access to or management of content in the repository is usually restricted to particular people or groups of people.
  • Forms and web scripts [32] Applications use HTML forms to create and update data. Content applications use forms to upload files from a user's local file system. HTML forms allow data to be submitted in one of two content types: URL-encoded (application-x-www-form-urlencoded) and multipart form data (multipart/form-data).
  • Internationalization (i18n) [33] Internationalization (often abbreviated to i18n) is an important consideration when developing a web script.
  • Java-backed web scripts [34] Java-backed web scripts are web scripts whose controller implementation is written in Java, rather than JavaScript.
Parent topic: Web scripts [35]

Understanding web scripts

Web scripts let you programmatically interact with the Alfresco Community Edition server. Unlike other interfaces exposed by Alfresco Community Edition, web scripts offer a RESTful API for content in the repository.

REST (Representational State Transfer) is an architectural style of which the web architecture is the most prominent example, one based on HTTP requests and responses, URIs (Uniform Resource Identifiers), and document types.

Web scripts let you implement your own RESTful API without tooling or Java knowledge. You simply need your favorite text editor. No compilation, generators, server restarts, or complex installs are required. This approach to developing an Alfresco Community Edition API means that web scripts offer many advantages over existing technologies, including ease and speed of development, and flexibility in API design.

By focusing on the RESTful architectural style and ease of development, web scripts let you build your own custom URI-identified and HTTP accessible content management web services backed by the Alfresco Community Edition server. This is like having an HTTP server with a built-in content repository allowing clients to easily access, manage, and cross-link content via a tailored RESTful interface designed specifically for the application requirements.

Parent topic: Repository-tier web scripts [24]

Web script types

A web script is a service bound to a URI that responds to HTTP methods such as GET, POST, PUT, and DELETE.
There are two kinds of web scripts that use the same underlying code:
  1. Data web scripts
  2. Presentation web scripts

  • Repository web scripts [36] Repository web scripts encapsulate access and modification of content/data held in the content repository; therefore, they are provided and exposed only by the Alfresco Community Edition server.
  • Presentation web scripts [37] Presentation web scripts let you customize and extend the web UI. They typically render HTML and can include browser-hosted JavaScript.
Parent topic: Repository-tier web scripts [24]

Repository web scripts

Repository web scripts encapsulate access and modification of content/data held in the content repository; therefore, they are provided and exposed only by the Alfresco Community Edition server.

Data web scripts provide a server interface for client applications to query, retrieve, update, and perform processes, typically using request and response formats such as XML and JSON.

Parent topic: Web script types [3]

Presentation web scripts

Presentation web scripts let you customize and extend the web UI. They typically render HTML and can include browser-hosted JavaScript.

Unlike data web scripts, presentation web scripts can be hosted in the Alfresco Community Edition server or in a separate presentation server. When hosted separately, presentation web scripts in the presentation server interact with data web scripts in the Alfresco content application server by using the Repository REST API.

Parent topic: Web script types [3]

Web Script Framework

The Web Script Framework is designed according to the Model View Controller (MVC) pattern (sometimes referred to as MVC for the web). While its primary design goal is to ensure that simple web scripts are easy to develop, advanced web scripts can support various features, such as rendering outputs in multiple languages, exposing and adhering to configuration options, and handling HTML form uploads.

You can call existing web scripts or create your own web scripts for new scenarios. For example, you can create your own web script to expose a RESTful interface onto a custom content repository extension.

  • Web script components [38] The Web Script Framework lets you create a web script using familiar technologies, such as scripting and template languages.
  • Naming conventions [11] Web script component file names adhere to the naming conventions defined by the Web Script Framework.
  • File locations [39] Web script component files are located in the file system within the Java classpath or in the repository.
  • URI anatomy [40] Web scripts are invoked through their defined URIs. Every web script URI follows the same form.
  • URI template [41] A URI template is a URI containing tokens that can be substituted with actual values. Tokens represent values to query parameters or values within the URI path where the syntax for expressing a token is {<token name>}.
  • Format readers [42] The Web Script Framework provides out-of-the-box format readers.
  • Response status code templates [43] Web scripts use response status code templates to render a custom response for a given status code. This is useful for providing unique information about a status code or to render a custom human readable interface.
Parent topic: Repository-tier web scripts [24]

Web script components

The Web Script Framework lets you create a web script using familiar technologies, such as scripting and template languages.

Each web script comprises only the following components:
  • A description document
  • An optional controller script
  • One or more FreeMarker response templates
Each component is implemented in its own file. The Web Script Framework dictates where the files are located and how they are named. This allows the framework to automatically locate and register web scripts without having to tell the framework where they are. In some cases, a web script can fall back to Java or rely on advanced Web Script Framework features where scripting alone cannot support the requirements of the web script.

Users of a web script only interact through the web script interface, which comprises its URI, HTTP method, and request/response document types. All of these are described in the web script description document, which is defined by the web script creator.

  • Web script description document [44] A web script description document is an XML file that describes the URI and HTTP method that initiates the web script. For example, the web script is given a short name and description, along with authentication and transactional needs. URI bindings are described as URI templates.
  • Web script controller script [45] A web script controller script is a JavaScript file that contains the actual logic of a web script.
  • Web script response template [46] Known as views, web script response templates render output in the correct format for specific needs, such as HTML, Atom, XML, RSS, JSON, CSV, or any combination of these.
Parent topic: Web Script Framework [27]
Related concepts
Description document [47]
Controller script [48]
Response template [49]

Web script description document

A web script description document is an XML file that describes the URI and HTTP method that initiates the web script. For example, the web script is given a short name and description, along with authentication and transactional needs. URI bindings are described as URI templates.

A detailed reference of elements in the web script description document can be found in the Web Script Description Language Reference [50].

An example of a web script description document follows:


<webscript>
  <shortname>Blog Search Sample</shortname>
  <description>Sample that finds all blog entries whose content contains the specified search term</description>
   <url>/sample/blog/search?q={searchTerm}</url>
   <url>/sample/blog/search.atom?q={searchTerm}</url>
   <url>/sample/b/s?q={searchTerm}</url>
   <url>/sample/b/s.atom?q={searchTerm}</url>
   <format default="html">extension</format>
   <authentication>guest</authentication>
   <transaction>required</transaction>
</webscript>

      
Parent topic: Web script components [38]

Web script controller script

A web script controller script is a JavaScript file that contains the actual logic of a web script.

The controller script can query the repository to build a set of data items, known as a model, to render in the response. It might also update the repository for URIs that intend to modify the repository (PUT, POST, and DELETE method bindings). The JavaScript has access to the URI query string, services, and repository data entry points.

        
// check that search term has been provided
if (args.q == undefined || args.q.length == 0)
{
   status.code = 400;
   status.message = "Search term has not been provided.";
   status.redirect = true;
}
else
{
   // perform search
   var nodes = search.luceneSearch("TEXT:" + args.q);
   model.resultset = nodes;
}        
        
      
Parent topic: Web script components [38]

Web script response template

Known as views, web script response templates render output in the correct format for specific needs, such as HTML, Atom, XML, RSS, JSON, CSV, or any combination of these.

The HTTP response is rendered by using one of the supplied templates, where the chosen template is based on the required response content type or status outcome. The template has access to the URI query string, common repository data entry points, and any data items built by the optional controller script.

        
<html>
  <body>
    <img src="${url.context}/images/logo/AlfrescoLogo32.png" alt="Alfresco" />
    Blog query: ${args.q}
    <br/>
    <table>
<#list resultset as node>
     <tr>
       <td><img src="${url.context}${node.icon16}"/></td>
       <td><a href="${url.serviceContext}/api/node/content/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/${node.name?url}">${node.name}</a></td>
     </tr>
</#list>
    </table>
  </body>
</html>        
        
      
Parent topic: Web script components [38]

Naming conventions

Web script component file names adhere to the naming conventions defined by the Web Script Framework.

Description documents

Web script description document file names have the following structure:

<web script id>.<http method>.desc.xml

  • <web script id> identifies the web script and must be unique within a web script package. A web script is uniquely identified by its web script package and web script ID. For example:/org/alfresco/tutorials/helloworld
  • <http method> specifies which HTTP method initiates the web script. Typically, this is GET, but other common methods include POST, PUT, and DELETE. A web script that only queries the repository is bound to the HTTP GET method.
  • All description document file names end with .desc.xml, indicating to the Web Script Framework the file is actually a description document that defines a web script. In the XML description document, web script descriptors have a root <webscript> element within which everything is defined.

The <shortname> and <description> elements provide human readable titles for the web script. You can see these in web script documentation and the web script index at: http://localhost:8080/alfresco/service/index

JavaScript based Controllers

Controller script file names have the following structure:

<web script id>.<http method>[.<format>].js

  • <web script id> identifies the web script and must be the same as the web script ID defined in the file name of the associated web script description document.
  • <http method> specifies which HTTP method will initiate the web script and must be the same as the associated web script description document.
  • <format> is an optional parameter in the controller name that can be used to specify a format of a POSTed request body. Out-of-the-box the Web Script framework provides the JSON (format = json), Atom Feed (format = atomfeed), Atom Entry (format = atomentry), and Atom (format = atom) formats. To get access to the POSTed data we can then use the json, feed, and entry JavaScript root objects in the controller. The Atom format provides both feed and entry root objects in the controller.
  • All controller script file names end with .js indicating to the Web Script Framework that the file is a controller script.

Java based Controllers

To bind a Spring bean to a Web Script it is only necessary to create a bean with an id of the following structure:

id="webscript.<packageId>.<web script id>.<httpMethod>"

FreeMarker Templates - View

Template file names have the following structure:

<web script id>.<httpMethod>.<format>.ftl

  • <web script id> identifies the web script and must be the same as the web script ID defined in the file name of the associated web script description document.
  • <http method> specifies which HTTP method will initiate the web script and must be the same as the associated web script description document.
  • <format> specifies the format of the response template whose value is one of the following:
    • html => text/html
    • text => text/plain
    • xml => text/xml
    • atom => application/atom+xml
    • atomentry => application/atom+xml;type=entry
    • atomfeed => application/atom+xml;type=feed
    • rss => application/rss+xml
    • json => application/json
    • opensearchdescription => application/opensearchdescription+xml
    • mediawiki => text/plain (Media Wiki markup)
    • portlet => text/html (head & tail chopped)
    • fbml => text/html
    • php => text/html
    • js => text/javascript
    • calendar => text/calendar
  • All template file names end with .ftl indicating to the Web Script Framework that the file is a FreeMarker template.
  • Multiple response format files can be used for a Web Script implementation.

FreeMarker Templates - Response status

Response status code document file names adhere to a naming convention as defined by the Web Script Framework. The appropriate response status code template is searched for in the following order:

  1. A template located in the same folder as the web script description document for rendering a specific status code response, which adheres to the naming convention <web script id>.<http method>.<format>.<status code>.ftl
  2. A template located in the same folder as the web script description document for rendering a response of any status code, which adheres to the naming convention <web script id>.<http method>.<format>.status.ftl
  3. A package-level template located in the package of the web script but, if not found, is searched for in the parent package hierarchy, up to the root package for rendering a response of any status code, which adheres to the naming convention <format>.status.ftl
  4. A template located in the root package for rendering an HTML response for the specific status code, which adheres to the naming convention <status code>.ftl
  5. A template located in the root package for rendering an HTML response of any status code, which adheres to the naming convention: status.ftl

Message Bundles

Web Script responses may be localized. Resource file names have the following structure:

<web script id>.<httpMethod>[_<locale>].properties

  • <web script id> identifies the web script and must be the same as the web script ID defined in the file name of the associated web script description document.
  • <http method> specifies which HTTP method will initiate the web script and must be the same as the associated web script description document.
  • _<locale> specifies the what localized bundle the file provides, such as for example French: helloworld.get_fr.properties
  • All message bundle file names end with .properties indicating to the Web Script Framework that the file belongs to a resource bundle.

Configuration

Configuration is accessed via the config root object, which is available during both controller script and template execution. Configuration file names have the following structure:

<web script id>.<httpMethod>.config.xml

  • <web script id> identifies the web script and must be the same as the web script ID defined in the file name of the associated web script description document.
  • <http method> specifies which HTTP method will initiate the web script and must be the same as the associated web script description document.
  • All configuration file names end with config.xml indicating to the Web Script Framework that the file is a file with external configuration.
Parent topic: Web Script Framework [27]

File locations

Web script component files are located in the file system within the Java classpath or in the repository.
The Web Script Framework searches for web scripts in the following order:
  • In the content repository under the folder /Company Home/Data Dictionary/Web Scripts Extensions
  • In the content repository under the folder /Company Home/Data Dictionary/Web Scripts
  • In the classpath under the folder /alfresco/extension/templates/webscripts
  • In the classpath under the folder /alfresco/templates/webscripts

Placing web scripts in the classpath lets you package and deploy them with other extensions that comprise your solution. You can install them using standard Alfresco Community Edition tools without having to upload them into the repository. However, it might not be as convenient to edit them while developing them as if they were located in the Alfresco content repository where you can easily edit them using Alfresco Share. You can also export and import web scripts in the content repository using the ACP (Alfresco Content Package) mechanism.

Note: For a default installation of Alfresco Community Edition, the classpath is located at <installLocation>/tomcat/shared/classes/alfresco/extension

A single Alfresco Community Edition server can contain hundreds of web scripts, each implemented with multiple files. To help manage all these web scripts, the Web Script Framework lets you organize web script component files into a hierarchical folder or package structure, similar to a Java package construct. Typically, the package name follows the reverse domain name pattern. For example, web scripts are all located in a folder named org/alfresco, which is reserved by Alfresco Community Edition.

Parent topic: Web Script Framework [27]

URI anatomy

Web scripts are invoked through their defined URIs. Every web script URI follows the same form.

For example:

http[s]://<host>:<port>/[<contextPath>/]/<servicePath>[/<scriptPath>] [?<scriptArgs>]

The host, port, and contextPath are all predefined by where the Alfresco Community Edition server is installed. By default, the contextPath is alfresco.

The Web Script Framework is mapped to servicePath. All Alfresco Community Edition server URL requests that start with /<contextPath>/<servicePath> trigger the Web Script Framework into action by assuming that a web script is to be invoked. By default, there are two variations of servicePath that are acceptable: /service and an abbreviated version /s.

Both of the following URIs will invoke a web script, in this case an admin call:

  • curl -uadmin:admin "http://localhost:8080/alfresco/service/api/admin/usage"
  • curl -uadmin:admin "http://localhost:8080/alfresco/s/api/admin/usage"

The scriptPath identifies the web script to invoke and is defined by the web script itself. It must be unique within an Alfresco Community Edition server. Duplicate URIs result in a web script registration failure and one of the URIs will have to be adjusted before successful registration. A scriptPath can be as simple or as complex as required and can comprise many path segments. For example, the CMIS web script URI to retrieve children of a folder residing in the repository contains the folder path. The following command line retrieves the children of the Data Dictionary folder as an Atom feed:

curl -uadmin:admin "http://localhost:8080/alfresco/s/cmis/p/Data%20Dictionary/children"

Finally, a web script URI can support query parameters as defined by the web script to control its behavior. For example, the CMIS web script to retrieve folder children can be restricted to return only documents, filtering out folders:

curl -uadmin:admin "http://localhost:8080/alfresco/s/cmis/p/Data%20Dictionary/children?types=documents"

There are some query parameters that apply to all web script invocations such as alf_ticket and format, which can be mixed with web script specific parameters:

curl -uadmin:admin "http://localhost:8080/alfresco/s/cmis/p/Data%20Dictionary/children?types=documents&format=atomfeed"

When in doubt over how to construct a URI for a given web script, consult its web script descriptor file, which you can find by using the web script index. The web script index can be displayed by directing your browser to the following URL:

http://localhost:8080/alfresco/service/index

Parent topic: Web Script Framework [27]

URI template

A URI template is a URI containing tokens that can be substituted with actual values. Tokens represent values to query parameters or values within the URI path where the syntax for expressing a token is {<token name>}.

An example of specifying a URI with two query parameters — one named "a" and the other named "b" is: /add?a={a}&amp;b={b}

Note: The query parameter delimiter "&" must be expressed as '&amp;' in web script descriptor documents, as "&" has special meaning within XML.

A client can generate the URI for invoking this web script when given the URI template and values for a and b. For example, if a is set to 1. and b is set to 2, the resulting URI is: /add?a=1&b=2

Query parameter tokens can indicate that the parameter is optional through the convention of appending a ‘?’ to the token name. For example, to indicate that the query parameter ‘b’ is optional, the URI template becomes: /add?a={a}&amp;b={b?}

Although you can mark parameters as optional, it is only a convention and the Web Script Framework does not enforce mandatory query parameters. This responsibility is given to the web script developer. An example of specifying a URI path with embedded tokens — one named ‘user’ and the other named ‘profilekind’ is: /user/{user}/profile/{profilekind}

Any URI that matches the URI template will invoke the web script that defines it. A match is made when:
  • All static parts of the URI template match the URI
  • All tokens within the URI template have been given values by the URI

For example, the following URIs match:

/user/joe/profile/public

/user/fred/profile/full

But the following URIs do not match:

/user/profile/public

/user/joe/profile

The value of a token in a URI path can itself have multiple path segments. For example, the following URI specifies the user value joe/smith and matches the previous URI template: /user/joe/smith/profile/public

When a URI request is made, the Web Script Framework locates the associated web script by finding the closest matching URI template for the URI. For example, consider that two web scripts each define their own similar URIs:
  • Web script A defines the URI template: /a/b
  • Web script B defines the URI template: /a/{z}

The URI /a/b invokes web script A, while the URI /a/c invokes web script B. Matching of static parts of the URI template takes precedence over matching a token value. The same token name can appear multiple times in a single URI template. Although rare, it is worth knowing the implications on matching to a web script. Consider the following URI template where the ‘user’ token is specified twice: /user/{user}/profile/{user}

For a match to occur, the value provided for each same named token must be the same.

This URI matches: /user/joe/profile/joe

But this URI does not match: /user/joe/profile/fred

Web script developers have access to the value provided for each token in both the controller script and response template.

Parent topic: Web Script Framework [27]

Format readers

The Web Script Framework provides out-of-the-box format readers.
  • JSON parses a request of MIME type application/json into a root object named json
  • Atom Feed parses a request of MIME type application/atom+xml;type=feed into a root object named feed whose type is an Apache Abdera Feed object
  • Atom Entry parses a request of MIME type application/atom+xml;type=entry into a root object named entry whose type is an Apache Abdera Entry object
  • Atom parses a request of MIME type application/atom+xml into a root object named either feed (Apache Abdera Feed) or entry (Apache Abdera Entry), depending on the request content
Parent topic: Web Script Framework [27]

Response status code templates

Web scripts use response status code templates to render a custom response for a given status code. This is useful for providing unique information about a status code or to render a custom human readable interface.

Response status code templates have access to the same root objects as normal web script response templates, except that the default templates <code>.ftl and status.ftl only have access to the root objects url, status, server, and date.

Note: When developing web scripts, leave the implementation of response status code templates until the end as the templates are not essential to web script execution. You can test without custom response status code templates as the Web Script Framework will always eventually find the default template status.ftl in the root package. As with all other response templates, adding and removing a response status code template requires you to register the web script again.
  • Response status codes [51] A web script uses a response status code to inform the calling client of its execution outcome.
Parent topic: Web Script Framework [27]

Response status codes

A web script uses a response status code to inform the calling client of its execution outcome.

The following scenarios can use status codes:

  • To inform the client of an error situation, such as an item not found in the repository.
  • To inform the client of an occurrence of an event, such as a new item has been created.
  • To instruct the client to perform a follow-up request, such as to ask for user name and password credentials.
  • To inform the client of success.

For example, the Folder Listing web script validates that the provided folder path actually exists in the repository using the following JavaScript in the controller script:


...
if (folder == undefined || !folder.isContainer) {
  status.code = 404;
  status.message = "Folder " + folderpath + " not found.";
  status.redirect = true;
}
...

The status root object is a special object provided to all controller scripts by the Web Script Framework. It allows a web script to specify the response status code along with an associated status message. Typically, the value of the status code is set to a standard HTTP status code.

It is useful when reporting error status codes to provide additional information about the error in the response, such as the cause of the error. To support this, the Web Script Framework allows for a custom status response template to be rendered, but this happens only if the status.redirect value is set to true. A default status response template is provided by the Web Script Framework, which renders everything known about the status, so it is not necessary to develop your own; however, you can create a custom status response template. If the value of status.redirect is set to false, the status code is set on the response, but the response template for the requested format is rendered anyway.

Parent topic: Response status code templates [43]

Invoking web scripts

A common client for invoking web scripts is a web browser, as many content rich applications are web applications. The web browser also provides an easy and convenient client for testing web scripts while developing them.

However, the web browser is not the exclusive client from which to invoke a web script. Any client capable of sending HTTP requests and receiving HTTP responses can be used. A good example is the cURL client that has full support for the HTTP protocol and is often used for testing the various capabilities of web scripts.

Although a client can use HTTP directly to invoke web scripts, the Web Script Framework also provides many helpers for invoking web scripts from environments that do not know HTTP. This allows the invocation of a web script using a mechanism that is natural to the calling environment and to the developer who knows the calling environment.

For example, helpers are provided that allow the following clients to naturally invoke web scripts:

  • Surf allows the invocation of a web script as if it were a Surf component, for example to create a Share dashlet
  • JSR-168 portal allows the invocation of a web script as if it were a JSR-168 portlet
  • JSF page allows the invocation of a web script as if it were a tag library

A carefully developed web script can be used from multiple environments without the need to change its implementation. For example, a web script for displaying your Alfresco Community Edition checked-out documents can be used standalone directly in a web browser, as a portlet in a JSR-168 portal, or as a dashlet in Alfresco Share.

Parent topic: Repository-tier web scripts [24]

Working with client limitations

Not all HTTP clients are equivalent in their capabilities. Many clients have limitations that mean certain HTTP features are not supported. Rather than dismiss those clients and reduce the scope of where web scripts can be invoked, the Web Script Framework provides helpers for working around those limitations.

These helpers include:

  • Tunneling HTTP methods
  • Forcing a successful HTTP response
  • Using JSON callbacks
  • Tunneling HTTP methods [52] Not all clients can issue all HTTP methods. In the most severe case, a client might be restricted to GET and POST only. In this situation, the Web Script Framework provides a mechanism for tunneling any HTTP method through a POST method. This is achieved by setting an override header named X-HTTP-Method-Override on the HTTP request whose value is the method name to invoke.
  • Forcing success response status [53] Not all clients can gracefully handle non-success HTTP response codes, such as the Adobe Flash runtime player, which is the runtime for Adobe Flex applications.
  • JSON callbacks [54] Web scripts that provide JSON responses are often invoked directly from within a web browser by using the XMLHttpRequest object. This is a technique popularly known as AJAX. For security reasons, solutions like these can run into cross-domain issues, a restriction that requires you to proxy your requests on the server side. Typically, to work around these issues, public services, such as Yahoo! JSON Services, provide a callback mechanism.
Parent topic: Repository-tier web scripts [24]

Tunneling HTTP methods

Not all clients can issue all HTTP methods. In the most severe case, a client might be restricted to GET and POST only. In this situation, the Web Script Framework provides a mechanism for tunneling any HTTP method through a POST method. This is achieved by setting an override header named X-HTTP-Method-Override on the HTTP request whose value is the method name to invoke.

For example, to invoke the following web script through an HTTP POST but inform the Web Script Framework to really perform a GET, you would type the following in the command line:

  • curl -uadmin:admin -d "" -H "X-HTTP-Method-Override:GET" http://localhost:8080/alfresco/s/api/admin/usage
Note: cURL's –d parameter informs cURL to perform an HTTP POST. The complete cURL manual can be found at http://curl.haxx.se/docs/manual.html [55].
In really unfortunate circumstances, some clients do not even support HTTP headers; therefore, the Web Script Framework also supports a query parameter named alf_method for representing the method to override.

For the equivalent of the override header, but expressed as a query parameter, you would type the following in the command line:

  • curl -uadmin:admin -d "" http://localhost:8080/alfresco/s/api/admin/usage?alf_method=GET

Tunneling HTTP methods is a last resort that should be used only when no other workaround is available. Each HTTP method has its own characteristics such as how it is cached, which HTTP clients and intermediaries expect. When tunneling these methods through HTTP POST, those expectations can no longer be met.

Note: If both the override header and query parameter are specified in the HTTP request, then the header takes precedence over the query parameter.

Method overrides are also supported when issuing HTTP GET requests through the alf_method query parameter. This is particularly useful for testing some non-GET methods by using the web browser.

Parent topic: Working with client limitations [29]

Forcing success response status

Not all clients can gracefully handle non-success HTTP response codes, such as the Adobe Flash runtime player, which is the runtime for Adobe Flex applications.

In this situation, web scripts provide a mechanism to force an HTTP response to indicate success in its response header; however, the response body will still represent the content as if a non-success status had occurred, allowing a client to interrogate error codes and messages, if provided by the web script.

To force success, the alf-force-success-response header is set on the HTTP request whose value is always set to true. For example, to force a success response status for a request to retrieve children of a folder that does not exist, you would type the following in the command line:

  • curl -uadmin:admin -v -H "alf-force-success-response:true" "http://localhost:8080/alfresco/s/cmis/p/doesnotexist"

Although the response status code is 200 (which means Success), the body of the response will still represent a failure and include details such as the real status code (in this case, 404, which means Not Found) and an error message.

Parent topic: Working with client limitations [29]

JSON callbacks

Web scripts that provide JSON responses are often invoked directly from within a web browser by using the XMLHttpRequest object. This is a technique popularly known as AJAX. For security reasons, solutions like these can run into cross-domain issues, a restriction that requires you to proxy your requests on the server side. Typically, to work around these issues, public services, such as Yahoo! JSON Services, provide a callback mechanism.
Note: A full description of the JSON callback mechanism can be found at http://developer.yahoo.com/javascript/json.html#callbackparam [56] on the Yahoo! Developer Network.

Web scripts also provide this mechanism, which wraps the JSON response text in parentheses and a function name of your choosing. A callback is invoked by adding the following URL query parameter to the web script request:

  • alf_callback=<function>

The function parameter specifies the name of a client-side JavaScript function to invoke.

Parent topic: Working with client limitations [29]

Exception handling in web scripts

Great care must be taken when using exception handling within a web script. If any exception expected to be handled by the repository is handled by the web script, this can lead to inconsistency of state, and potentially corruption of the repository.

As a web script executes it will perform operations such as creating a new document in the repository. While it seems logical to handle possible exceptions, such as failure to create a document (possibly due to permissions, or the existence of a document with the same name in the same folder), this should be avoided at the web script level. Such exceptions will be handled appropriately by the repository. In practice you should only carry out exception handling for exceptions that you know are not handled at a lower layer of Alfresco Community Edition.

Parent topic: Repository-tier web scripts [24]

Caching

A key aspect of HTTP is its ability to support caching of HTTP responses, relieving workload on the HTTP server, which does not have to re-create the HTTP response for every request. From a client perspective this gives a prompt response.
The Web Script Framework complies with HTTP caching, in particular with the notions of Last Modified Time and ETag (a kind of hash), allowing the caching of Web script responses using HTTP-aware caches.
Note: An ETag (entity tag) is a response header used to determine change in content at a given URL. Clients and caches use the ETag in subsequent requests to determine with the server if the content needs refreshing.

The Web Script Framework does not invent its own caching approach but relies on the caching protocol defined by HTTP. Each web script specifies how it is to be cached, which the Web Script Framework translates into appropriate HTTP headers when it is invoked. A third party HTTP cache that is deployed as part of the application then caches the web script response.

It is often necessary to cache the retrieval of content streams of documents residing in the repository as these can be large in size. A typical setup to support this scenario (as shown in the following figure) is to place an HTTP cache proxy between the client (for example, a web browser) and the Alfresco Community Edition server.

A pre-built, out-of-the-box web script exists for retrieving the content stream of a document residing in the repository. This web script is CMIS compliant and also specifies its HTTP caching requirements. With the HTTP cache proxy deployed, the content responses are cached intelligently and the cache is only updated when the content is updated in the repository. This setup will also cache all other responses from web scripts that indicate how they are to be cached.

When developing a web script, you can specify its caching requirements, such as how long to keep the response in the cache or how to calculate the hash for the response. It is important to note that the Web Script Framework does not actually perform any caching. Instead, Alfresco Community Edition relies on one of the many HTTP caches already available, such as Squid (www.squid-cache.org), an HTTP caching proxy. Therefore, you must either embed an HTTP cache in your client or deploy an HTTP-cache proxy in front of the Alfresco Community Edition server to enable caching.

  • Runtime cache controls [57] Some cache controls can be set only during the execution of a web script, such as setting when the content of the response was last modified. To support this, the Web Script Framework provides a special root object named cache to all controller scripts for allowing cache controls to be set at runtime.
  • Descriptor cache controls [58] When developing a web script, you can specify whether its response is to be cached and, if so, how it is to be cached through the web script descriptor document.
Parent topic: Repository-tier web scripts [24]
Related concepts
Runtime cache controls [59]
Descriptor cache controls [60]
Related tasks
Creating a web script using cache controls [22]

Runtime cache controls

Some cache controls can be set only during the execution of a web script, such as setting when the content of the response was last modified. To support this, the Web Script Framework provides a special root object named cache to all controller scripts for allowing cache controls to be set at runtime.

The cache root object provides the following API:

neverCache (read/write Boolean)
Controls whether web script response should be cached at all; true means never cache. If not set, the default value is specified by the cache control section of the web script descriptor.
isPublic (read/write Boolean)
Controls whether web script response should be cached by public caches. If not set, the default value is specified by the cache control section of the web script descriptor.
mustRevalidate (read/write Boolean)
Controls whether cache must revalidate its version of the web script response to ensure freshness. If not set, the default value is specified by the cache control section of the web script descriptor.
maxAge (read/write long)
Specifies the maximum amount of time (in seconds, relative to the time of request) that the response will be considered fresh. If not set, the default value is null.
lastModified (read/write date)
Specifies the time that the content of the response last changed. If not set, the default value is null.
ETag (read/write string)
Specifies a unique identifier that changes each time the content of the response changes. If not set, the default value is null.
Parent topic: Caching [15]

Descriptor cache controls

When developing a web script, you can specify whether its response is to be cached and, if so, how it is to be cached through the web script descriptor document.

The optional <cache> element of the web script descriptor provides the following cache flags:

never
(Optional) Specifies whether caching should be applied at all. If true, the web script response should never be cached; otherwise, the web script response can be cached.
public
(Optional) Specifies whether authenticated responses should be cached in a public cache. If true, the web script response should never be cached; otherwise, the web script response can be cached.
mustrevalidate
(Optional) Specifies whether a cache must revalidate its version of the web script response in order to ensure freshness. If true, the cache must revalidate; otherwise, the cache can revalidate.
For example, the following web script descriptor specifies that responses can be cached, but never in a public cache as the response requires authentication, and that the cache must revalidate to ensure freshness of the content.


<webscript>
  <shortname>Design time cache sample</shortname>
  <url>/cache</url>
  <authentication>user</authentication>
  <cache>
    <never>false</never>
    <public>false</public>
    <mustrevalidate/>
</cache>
</webscript>

Parent topic: Caching [15]

Authenticating web scripts

You can invoke a web script without first authenticating, that is, without specifying a user name and password as identification. This is rare when interacting with the Alfresco Community Edition server as access to or management of content in the repository is usually restricted to particular people or groups of people.

To support restricted access, a web script can specify its authentication requirements. There are four levels of required authentication:

None
The web script does not require any authentication to be invoked.
Guest
The web script can be invoked by a guest user of the Alfresco Community Edition server.
User
The web script must be invoked by a named user known to the Alfresco Community Edition server.
Admin
The web script must be invoked by a named user who is an administrator of the Alfresco Community Edition server.

An authenticated web script has access to all the services of the Alfresco Community Edition server and thus can perform any operation, although it still adheres to the permissions of the authenticated user.

JSR-168 Authenticator

JSR-168 Authenticator only works if running on the repository tier, and it does not work for web scripts running in the Share tier. Surf has support for JSR-168 portlets built-in.

  • Custom client authentication [61] HTTP Basic authentication is a method designed to allow a web browser or other client program to provide credentials in the form of a user name and password when making an HTTP request.
Parent topic: Repository-tier web scripts [24]
Related tasks
Creating a Hello World web script [62]
Specifying user identity: HTTP Authentication, Tickets, Guest [63]
Related reference
Web script description markup for authentication [64]

Custom client authentication

HTTP Basic authentication is a method designed to allow a web browser or other client program to provide credentials in the form of a user name and password when making an HTTP request.

If you are using the Alfresco checked-out documents web script as a JSR-168 portlet configured into your portal, when you launch the portal the portal itself asks you to log in. The web script needs to know who is authenticated, so the Web Script Framework communicates with the portal to determine the currently authenticated user. When the web script is rendered in the portal page, the web script is invoked as the portal user.

Behind the scenes, the Web Script Framework chooses the most appropriate option for specifying the user identity, either HTTP Basic authentication, ticket, or guest when invoking the web script. The same mechanism is used for Alfresco Share.

Parent topic: Authenticating web scripts [31]

Forms and web scripts

Applications use HTML forms to create and update data. Content applications use forms to upload files from a user's local file system. HTML forms allow data to be submitted in one of two content types: URL-encoded (application-x-www-form-urlencoded) and multipart form data (multipart/form-data).

Web scripts can handle URL-encoded submissions as other requests, where the web script parses the URI to extract the form data. However, the URL-encoded approach is inefficient for sending large quantities of binary data or text containing non-ASCII characters.

To submit forms containing files, non-ASCII, and binary data, the multipart form data content type must be used; however, this type of request is not as simple to parse for the server. Given the common requirement to submit files to the repository, the Web Script Framework provides explicit support for handling multipart form data submissions by hiding the complexity of request parsing from the developer of the web script.

Related tasks:

Multipart forms tutorial [65]

Parent topic: Repository-tier web scripts [24]

Internationalization (i18n)

Internationalization (often abbreviated to i18n) is an important consideration when developing a web script.

For human-readable web script responses it is often necessary to render the output in the preferred language of the user or the preferred language of the client. This means that human-readable text cannot be placed directly in the web script response template.

Therefore, the Web Script Framework uses the common practice of allowing text to be placed into resource bundles, where a resource bundle exists for each supported language.

  • Creating resource bundles supporting i18n [66] The Web Script Framework allows text to be placed into resource bundles, where a resource bundle exists for each supported language.
  • Adding resource bundles for additional languages [67] Once you have created and registered your web script, you can add additional resource bundles for other languages.
  • Overriding the default message bundle [68] To quickly provision your site for many different countries and languages, you can provide a message bundle for the Alfresco Share configuration. To do so, you need to wire in your own message bundle to Share that overrides Share’s default message bundle values.
Parent topic: Repository-tier web scripts [24]

Creating resource bundles supporting i18n

The Web Script Framework allows text to be placed into resource bundles, where a resource bundle exists for each supported language.
This task creates a simple web script that renders an HTML message.
  1. Log in to Alfresco Share:
    1. Open a web browser and enter the URL: http://localhost:8080/share
    2. If prompted, log in with the user name admin and password admin.
  2. Navigate to Data Dictionary > Web Scripts Extensions > org > example.
  3. Create a web script description document for your i18n sample:
    1. In the Create menu, select XML.
    2. Enter the name for the web script in the Name field: i18n.get.desc.xml
    3. Type the following in the content box:

      <webscript>
        <shortname>I18n Sample</shortname>
        <description>Internationalization Sample</description>
        <url>/i18n</url>
      </webscript>
    4. Click Create.
    5. Navigate back to the org/example folder using the breadcrumb trail.
  4. Create a default message bundle for your i18n sample:
    1. In the Create menu, select Plain Text.
    2. Enter the name in the Name field: i18n.get.properties
    3. Type the following in the content box:

      greeting=Hello
      farewell=Goodbye
    4. Click Create.
  5. Create a response template for your i18n sample:
    1. In the Create menu, select Plain Text.
    2. Enter the name in the Name field: i18n.get.html.ftl
    3. Type the following in the content box:

      ${msg("greeting")}. ${msg("farewell")}
    4. Click Create.
    5. Navigate back to org/example using the breadcrumb trail.
  6. Register the i18n web script with Alfresco Community Edition.
    1. In a web browser tab, enter the URL: http://localhost:8080/alfresco/service/index
    2. If prompted, log in with the user name admin and password admin.
    3. Click Refresh Web Scripts. A message indicates there is one additional web script.
  7. Test your response template to ensure it is rendering values from the default resource bundle by type the following in your command line: curl "http://localhost:8080/alfresco/service/i18n"

    The response is: Hello. Goodbye.

The web script response template uses the msg method to render text whose value is taken from the resource bundle associated with the required language. Resource bundles contain one or more messages, each identified by a name; this is the name passed to the msg method. The example refers to the messages greeting and farewell.

Each resource bundle adheres to the naming convention defined by the Web Script Framework, which are structured as follows: <web script id>.<http method>[_<locale>].properties

The <web script id> identifies the web script and must be the same as the web script ID defined in the file name of the associated Web script description document. The <http method> specifies which HTTP method will initiate the web script and must be the same as the associated web script description document.

The optional <locale> identifies the language for which this resource bundle applies. If not specified, the resource bundle is treated as the fallback set of values if no other relevant resource bundle for the required language can be found.

Finally, all resource bundle file names end with .properties. This indicates to the Web Script Framework that the file is a resource bundle.

Parent topic: Internationalization (i18n) [33]

Adding resource bundles for additional languages

Once you have created and registered your web script, you can add additional resource bundles for other languages.
This task adds another resource bundle for the German language.
  1. Log in to Alfresco Share:
    1. Open a web browser and enter the URL: http://localhost:8080/share
    2. If prompted, log in with the user name admin and password admin.
  2. Click on the Repository link on the Share header.
  3. Navigate to Data Dictionary > Web Scripts Extensions > org > example.
  4. Create a German resource bundle for your i18n sample:
    1. In the Create menu, select Plain Text.
    2. Enter the name for the web script in the Name field: i18n.get_de.properties
    3. Type the following in the content box:

      greeting=Guten Tag
      farewell=Auf Wiedersehen
    4. Click Create.
    5. Navigate back to the org/example folder using the breadcrumb trail.
  5. Re-register the i18n web script with Alfresco Community Edition.
    1. In a web browser, enter the URL: http://localhost:8080/alfresco/service/index
    2. If prompted, log in with the user name admin and password admin.
    3. Click Refresh Web Scripts.

      This time you have created a resource bundle for the German language as identified by the locale of de. Locales are specified as follows: <language>[_<country>][_<variant>]

      The language argument is a valid ISO language code, which is a lowercase, two-letter code as defined by ISO-639. The optional country argument is a valid ISO country code, which is an uppercase, two-letter code as defined by ISO-3166. Finally, the optional variant argument is a vendor-or web browser–specific code.

  6. Test your response template to ensure it is rendering values from the German resource bundle by typing the following in your command line: curl -H "Accept-Language: de" "http://localhost:8080/alfresco/service/i18n"

    The response is: Guten Tag. Auf Wiedersehen.

A client specifies its preferred language through the HTTP header named Accept-Language, to which the Web Script Framework adheres.
Parent topic: Internationalization (i18n) [33]

Overriding the default message bundle

To quickly provision your site for many different countries and languages, you can provide a message bundle for the Alfresco Share configuration. To do so, you need to wire in your own message bundle to Share that overrides Share’s default message bundle values.

  1. Define a Spring bean that overrides Alfresco Share's default message bundle so it includes your custom bundle.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 
   'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>

   <bean id="webscripts.resources"   
         class="org.alfresco.i18n.ResourceBundleBootstrapComponent">

      <property name="resourceBundles">
         <list>
            <value>alfresco.messages.webscripts</value>
            <value>alfresco.messages.slingshot</value>
            <value>alfresco.web-extension.messages.kbsite</value>
         </list>
      </property>
   </bean>

</beans>

This Spring bean adds support for an additional message bundle called kbsite.properties located under web-extension/messages. In this message bundle, you might define the following key/value pairs:

page.kbSiteDashboard.title=Knowledge Base Site Dashboard
page.kbSiteDashboard.description=Knowledge Base site's dashboard page
title.kbSite=Knowledge Base Site

These are the same keys that the preset configuration and web script were looking for. You can now fully internationalize your new site preset. You can provide bundles so that the Create Site wizard works for languages such as Spanish or Mandarin Chinese.

Parent topic: Internationalization (i18n) [33]

Java-backed web scripts

Java-backed web scripts are web scripts whose controller implementation is written in Java, rather than JavaScript.

Java-backed web scripts are useful when you want to:

  • Access Alfresco Community Edition not available by using the JavaScript API
  • Interact with systems whose only API is exposed by using Java
  • Override how responses are rendered, such as to stream large content
  • Ensure that performance is absolutely critical

Unlike scripted web scripts, Java-backed web scripts require more tooling for their development as you must compile the Java source code, package, and deploy to the Alfresco Community Edition server.

A Java-backed web script is constructed like a scripted web script, except that a Java class replaces the controller script. It still has the same intent of encapsulating the behavior of the web script and producing a model for subsequent rendering by a response template. Alfresco Community Edition is aware of the Java class through Spring Framework configuration, which identifies the Java class as being the behavior for the web script. All other components are exactly the same as those for scripted web scripts.

  • Java approach to web scripts [69] The Java class for a Java-backed web script has to follow one rule: it must implement the Java interface: org.alfresco.web.scripts.WebScript
Parent topic: Repository-tier web scripts [24]

Java approach to web scripts

The Java class for a Java-backed web script has to follow one rule: it must implement the Java interface: org.alfresco.web.scripts.WebScript

This interface defines the following two methods that must be implemented:


/**
* Gets the Web script Description
*
* @return the Web script description
*/
public WebScriptDescription getDescription();
/**
* Execute the Web script
*
* @param req the Web script request
* @param res the Web script response
*/
public void execute(WebScriptRequest req, WebScriptResponse res) throws
IOException;

The first method, getDescription(), returns a WebScriptDescription object, which is a Java representation of the web script description XML document. The second method, execute(), is invoked by the Web Script Framework to initiate the web script.

The Web Script Framework also provides two Java classes that implement the difficult parts of this interface, which you can extend as a starting point. The simplest helper Java class is named as follows: org.alfresco.web.scripts.AbstractWebScript

This helper provides an implementation of getDescription() but does not provide any execution assistance, which it delegates to its derived class. This allows a Java-backed web script to take full control of the execution process, including how output is rendered to the response.

The other helper Java class is named: org.alfresco.web.scripts.DeclarativeWebScript

This helper provides an implementation of getDescription() and execute(). It encapsulates the execution of a scripted web script, which is:
  • Locate an associated controller script written in JavaScript and, if found, execute it.
  • Locate an associated response template for the requested format and execute it, passing the model populated by the controller script.

By default, all web scripts implemented through scripting alone are backed by the DeclarativeWebScript Java class. There is one special hook point that makes this a useful class for your own Java-backed web scripts to extend. Prior to controller script execution, DeclarativeWebScript invokes the template method executeImpl(), which it expects derived Java classes to implement.

protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)

This is where the behavior of a custom Java-backed web script is encapsulated, including the population of the web script model, which is returned from this method.

The Java Folder Listing web script uses DeclarativeWebScript for its starting point.

        
... 
public class JavaDir extends DeclarativeWebScript
{

    ...
    protected Map<String, Object> executeImpl(WebScriptRequest req, Status status,
      Cache cache)
    {

    ...
    return model;
    }
    ...
}

The model returned from executeImpl() is passed to the response template for subsequent rendering. Prior to template rendering, the model can also be accessed and further refined by a controller script, if one happens to be provided for the web script. Apart from implementing the WebScript interface, there are no other web script demands on the Java class. You can give the Java class any name and place it in any Java package.

Parent topic: Java-backed web scripts [34]

Source URL: https://docs.alfresco.com/community/references/dev-extension-points-webscripts.html

Links:
[1] http://docs.alfresco.com/support/concepts/su-product-lifecycle.html
[2] https://docs.alfresco.com/../concepts/dev-platform-arch.html
[3] https://docs.alfresco.com/../concepts/ws-types.html
[4] https://en.wikipedia.org/wiki/Representational_state_transfer
[5] https://docs.alfresco.com/../pra/1/topics/pra-welcome.html
[6] https://docs.alfresco.com/../concepts/surf-connectors-endpoints.html
[7] https://docs.alfresco.com/dev-extension-points-content-model.html
[8] https://en.wikipedia.org/wiki/Word_stem
[9] https://en.wikipedia.org/wiki/Root_%28linguistics%29
[10] https://github.com/Alfresco/alfresco-sdk/blob/master/docs/getting-started.md
[11] https://docs.alfresco.com/../concepts/ws-component-name.html
[12] https://docs.alfresco.com/API-JS-rootscoped.html
[13] https://docs.alfresco.com/API-FreeMarker-defaultmodel.html
[14] https://docs.alfresco.com/../concepts/ws-presentation-locations.html
[15] https://docs.alfresco.com/../concepts/ws-caching-about.html
[16] https://docs.alfresco.com/../concepts/dev-extensions-share-surf-web-scripts.html
[17] https://docs.alfresco.com/dev-extension-points-data-lists.html
[18] https://github.com/Alfresco/alfresco-sdk-samples/tree/alfresco-51/all-in-one/add-web-script-repo
[19] https://github.com/Alfresco/alfresco-sdk-samples/tree/alfresco-51/all-in-one/custom-data-list-repo
[20] http://ecmarchitect.com/alfresco-developer-series-tutorials/webscripts/tutorial/tutorial.html
[21] https://docs.alfresco.com/../tasks/ws-config.html
[22] https://docs.alfresco.com/../tasks/ws-cache-using.html
[23] https://docs.alfresco.com/../tasks/ws-request-process.html
[24] https://docs.alfresco.com/../concepts/ws-overview.html
[25] https://docs.alfresco.com/../concepts/dev-platform-extension-points.html
[26] https://docs.alfresco.com/../concepts/ws-intro.html
[27] https://docs.alfresco.com/../concepts/ws-framework.html
[28] https://docs.alfresco.com/../concepts/ws-invoke-where.html
[29] https://docs.alfresco.com/../concepts/ws-client-limitations.html
[30] https://docs.alfresco.com/../concepts/ws-exception-handling.html
[31] https://docs.alfresco.com/../concepts/ws-authenticating.html
[32] https://docs.alfresco.com/../concepts/ws-forms-about.html
[33] https://docs.alfresco.com/../concepts/ws-I18N.html
[34] https://docs.alfresco.com/../concepts/ws-java-backed-webscripts.html
[35] https://docs.alfresco.com/../references/dev-extension-points-webscripts.html
[36] https://docs.alfresco.com/../concepts/ws-types-data.html
[37] https://docs.alfresco.com/../concepts/ws-types-presentation.html
[38] https://docs.alfresco.com/../concepts/ws-components.html
[39] https://docs.alfresco.com/../concepts/ws-component-place.html
[40] https://docs.alfresco.com/../concepts/ws-anatomy.html
[41] https://docs.alfresco.com/../concepts/ws-uri-template.html
[42] https://docs.alfresco.com/../concepts/ws-format-reader.html
[43] https://docs.alfresco.com/../concepts/ws-resp-code-template.html
[44] https://docs.alfresco.com/../concepts/ws-desc-doc.html
[45] https://docs.alfresco.com/../concepts/ws-controll-script.html
[46] https://docs.alfresco.com/../concepts/ws-resp-template.html
[47] https://docs.alfresco.com/ws-desc-doc.html
[48] https://docs.alfresco.com/ws-controll-script.html
[49] https://docs.alfresco.com/ws-resp-template.html
[50] https://docs.alfresco.com/../references/api-wsdl-webscript-descriptor-language-reference.html
[51] https://docs.alfresco.com/../concepts/ws-resp-code-set.html
[52] https://docs.alfresco.com/../concepts/ws-tunneling-http-methods.html
[53] https://docs.alfresco.com/../concepts/ws-forcing-success.html
[54] https://docs.alfresco.com/../concepts/ws-json-callbacks.html
[55] http://curl.haxx.se/docs/manual.html
[56] http://developer.yahoo.com/javascript/json.html#callbackparam
[57] https://docs.alfresco.com/../concepts/ws-runtime-cache-controls.html
[58] https://docs.alfresco.com/../concepts/ws-desc-cache-controls.html
[59] https://docs.alfresco.com/ws-runtime-cache-controls.html
[60] https://docs.alfresco.com/ws-desc-cache-controls.html
[61] https://docs.alfresco.com/../concepts/ws-custom-client-authentication.html
[62] https://docs.alfresco.com/../tasks/ws-hello-world-create.html
[63] https://docs.alfresco.com/../tasks/ws-specify-user-identity.html
[64] https://docs.alfresco.com/../references/api-wsdl-authentication.html
[65] https://docs.alfresco.com/../tasks/ws-forms-process.html
[66] https://docs.alfresco.com/../tasks/ws-I18N.html
[67] https://docs.alfresco.com/../tasks/ws-I18N-german.html
[68] https://docs.alfresco.com/../concepts/kb-preset-internationalization.html
[69] https://docs.alfresco.com/../concepts/ws-and-Java.html