eXo Platform 3.5

## Chapter 4. eXo Web Services

The Web Services module allows eXo technology to integrate with external products and services.

It is implementation of API for RESTful Web Services with extensions, Servlet and cross-domain AJAX web-frameworks and JavaBean-JSON transformer.

## 4.1. Introduction to the Representational State Transfer (REST)

Representational State Transfer (REST) is a style of software architecture for distributed hypermedia systems such as the World Wide Web. The term was introduced in the doctoral dissertation in 2000 by Roy Fielding, one of the principal authors of the Hypertext Transfer Protocol (HTTP) specification, and has come into widespread use in the networking community.

REST strictly refers to a collection of network architecture principles that outline how resources are defined and addressed. The term is often used in a looser sense to describe any simple interface that transmits domain-specific data over HTTP without an additional messaging layer such as SOAP or session tracking via HTTP cookies.

The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author's hypertext reference must fit within the definition of a resource. A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.

REST uses a resource identifier to identify the particular resource involved in an interaction between components. REST connectors provide a generic interface for accessing and manipulating the value set of a resource, regardless of how the membership function is defined or the type of software that is handling the request. URL or URN are the examples of a resource identifier.

REST components perform actions with a resource by using a representation to capture the current or intended state of that resource and transferring that representation between components. A representation is a sequence of bytes, plus representation metadata to describe those bytes. Other commonly used but less precise names for a representation include: document, file, and HTTP message entity, instance, or variant. A representation consists of data, metadata describing the data, and, on occasion, metadata to describe the metadata (usually for the purpose of verifying message integrity). Metadata are in the form of name-value pairs, where the name corresponds to a standard that defines the value's structure and semantics. The data format of a representation is known as a media type.

Data Element | Modern Web Examples
--- | ---
resource | the intended conceptual target of a hypertext reference
resource identifier | URL, URN
representation | HTML document, JPEG image
representation metadata | media type, last-modified time
resource metadata | source link, alternates, vary
control data | if-modified-since, cache-control

REST uses various connector types to encapsulate the activities of accessing resources and transferring resource representations. The connectors present an abstract interface for component communication, enhancing simplicity by providing a complete separation of concepts and hiding the underlying implementation of resources and communication mechanisms.

Connector | Modern Web Examples
--- | ---
client | libwww, libwww-perl
server | libwww, Apache API, NSAPI
cache | browser cache, Akamai cache network
resolver | bind (DNS lookup library)
tunnel | SOCKS, SSL after HTTP CONNECT

The primary connector types are client and server. The essential difference between the two is that a client initiates communication by making a request, whereas a server listens for connections and responds to requests in order to supply access to its services. A component may include both client and server connectors.

An important part of RESTful architecture is a well-defined interface to communicate, in particular it is a set of HTTP methods such as POST, GET, PUT and DELETE. These methods are often compared with the CREATE, READ, UPDATE, DELETE (CRUD) operations associated with database technologies. An analogy can also be made:

- PUT is analogous to CREATE or PASTE OVER,

- GET to READ or COPY,

- POST to UPDATE or PASTE AFTER, and

- DELETE to DELETE or CUT.

### Note

RESTful architecture is not limited to those methods, one of good examples of extension is the WebDAV protocol.

The CRUD (Create, Read, Update and Delete) verbs are designed to operate with atomic data within the context of a database transaction. REST is designed around the atomic transfer of a more complex state and can be viewed as a mechanism for transferring structured information from one application to another.

HTTP separates the notions of a web server and a web browser. This allows the implementation of each to vary from the other based on the client/server principle. When used RESTfully, HTTP is stateless. Each message contains all the information necessary to understand the request.

As a result, neither the client nor the server needs to remember any communication-state between messages. Any state retained by the server must be modeled as a resource.

## 4.2. Overwrite default providers

This section will show you how to overwrite the default providers in eXo JAX-RS implementation.

### 4.2.1. Motivation

There is set of providers embedded in eXo JAX-RS implementation.

Implementations of MessageBodyReader and MessageBodyWriters are taking care about serialization/deserialization of message body (HTTP request/response's body).

The next set of media and Java types processed automatically, thanks to embedded Readers (Writers).

Media Type | Java Type
--- | ---
*/* | byte[]
*/* | javax.activation.DataSource
*/* | java.io.File
*/* | java.io.InputStream
*/* | java.io.Reader
*/* | java.lang.String
*/* | javax.ws.rs.core.StreamingOutput (Writer ONLY)
application/json | 1. Object with simple constructor + get/set methods; 2. Java Collection (java.uitl.List<T>, java.uitl.Set<T>, java.util.Map<String, T>, etc) where T as described in 1.
application/x-www-form-urlencoded | javax.ws.rs.core.MultivaluedMap<String, String>
multipart/* | java.util.Iterator<org.apache.commons.fileupload.FileItem>
application/xml, application/xhtml+xml, text/xml | javax.xml.bind.JAXBElement
application/xml, application/xhtml+xml, text/xml | Object with JAXB annotations
application/xml, application/xhtml+xml, text/xml | javax.xml.transform.stream.StreamSource
application/xml, application/xhtml+xml, text/xml | javax.xml.transform.sax.SAXSource
application/xml, application/xhtml+xml, text/xml | javax.xml.transform.dom.DOMSource

In some case it may be required to use alternative provider for the same media and java type but such changes must not impact to any other services.

### 4.2.2. Usage

To be able overwrite default JAX-RS provider(s) developer need:

1. Deploy own RESTful service(s) by using subclass of javax.ws.rs.core.Application (hereinafter Application).

2. Service(s) NOT NEED to implement marker interface ResourceContainer and MUST NOT be configured as component(s) of eXo Container. Instead of it Application must be configured as component of eXo Container.

3. If RESTful services or providers require some dependencies from eXo Container then Application should inject it by own constructor and then delegate to services or providers. As alternative method getClasses() may be used for deliver services/providers classes instead of instances. In this case services/providers will work in per-request mode and RESTful framework will take care about resolving dependencies.

### 4.2.3. Example

In example below see how to use Jackson JSON provider instead of embedded in eXo RESTful framework.

Create subclass of javax.ws.rs.core.Application with code as bellow and add it to the eXo Container configuration.

    package org.exoplatform.test.jackson;

    import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;

    import java.util.HashSet;

    import java.util.Set;

    import javax.ws.rs.core.Application;

    public class Application1 extends Application

    {

       @Override

       public Set<Class<?>> getClasses()

       {

          Set<Class<?>> cls = new HashSet<Class<?>>(1);

          cls.add(Resource1.class);

          return cls;

       }

       @Override

       public Set<Object> getSingletons()

       {

          Set<Object> objs = new HashSet<Object>(1);

          objs.add(new JacksonJaxbJsonProvider());

          return objs;

       }

    }

In this example we assumes Resource1 is Java class which carries JAX-RS annotations and it uses JSON. In this case RESTful framework will use JacksonJaxbJsonProvider for serializing/deserializing of all messages to/from Resource1. But it will not impact to other services in any kind.

## 4.3. RestServicesList Service

RestServicesList service provides information about REST services deployed to the application server.

- Path - path to service

- Regex - service's URL regular expression

- FQN - full qualified name of service's class

The list can be provided in two formats: HTML and JSON.

### 4.3.1. Usage

### Note

Class does not implement org.exoplatform.services.rest.resource.ResourceContainer and must never be binded to RESTful framework by using eXoContainer. This service must works as per-request resource.

#### HTML format

To get the list of services in HTML format use listHTML() method:

    @GET

    @Produces({MediaType.TEXT_HTML})

    public byte[] listHTML()

    {

       ...

    }

To do this, perform a simple GET request to the RestServicesList link.

f.e. curl -u root:exo http://localhost:8080/rest/ will return such HTML code:

    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">

       <head>

          <title>eXo JAXRS Implementation</title>

       </head>

       <body>

          <h3 style="text-align:center;">Root resources</h3>

          <table   width="90%"   style="table-layout:fixed;">

             <tr>

                <th>Path</th>

                <th>Regex</th>

                <th>FQN</th>

             </tr>

             <tr>

                <td>script/groovy</td>

                <td>/script/groovy(/.*)?</td>

                <td>org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoader</td>

             </tr>

             <tr>

                <td>/lnkproducer/</td>

                <td>/lnkproducer(/.*)?</td>

                <td>org.exoplatform.services.jcr.webdav.lnkproducer.LnkProducer</td>

             </tr>

             <tr>

                <td>/registry/</td>

                <td>/registry(/.*)?</td>

                <td>org.exoplatform.services.jcr.ext.registry.RESTRegistryService</td>

             </tr>

             <tr>

                <td>/jcr</td>

                <td>/jcr(/.*)?</td>

                <td>org.exoplatform.services.jcr.webdav.WebDavServiceImpl</td>

             </tr>

             <tr>

                <td>/</td>

                <td>(/.*)?</td>

                <td>org.exoplatform.services.rest.ext.service.RestServicesList</td>

             </tr>

          </table>

       </body>

    </html>

If you perform the same request with your browser, you'll see the table with the list of deployed services like this:

Path | Regex | FQN
--- | --- | ---
script/groovy | /script/groovy(/.*)? | org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoader
/lnkproducer/ | /lnkproducer(/.*)? | org.exoplatform.services.jcr.webdav.lnkproducer.LnkProducer
/registry/ | /registry(/.*)? | org.exoplatform.services.jcr.ext.registry.RESTRegistryService
/jcr | /jcr(/.*)? | org.exoplatform.services.jcr.webdav.WebDavServiceImpl
/ | (/.*)? | org.exoplatform.services.rest.ext.service.RestServicesList

#### JSON format

To get the list of services in HTML format use listJSON() method:

    @GET

    @Produces({MediaType.APPLICATION_JSON})

    public RootResourcesList listJSON()

    {

       ...

    }

To do this, add "Accept:application/json" header to your GET request

f.e. curl -u root:exo http://localhost:8080/rest/ -H "Accept:application/json" will return such JSON:

    {"rootResources":[
       {
          "fqn":"org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoader",
          "regex":"/script/groovy(/.*)?",
          "path":"script/groovy"
       },
       {
          "fqn":"org.exoplatform.services.jcr.webdav.lnkproducer.LnkProducer",
          "regex":"/lnkproducer(/.*)?",
          "path":"/lnkproducer/"
       },
       {
          "fqn":"org.exoplatform.services.jcr.ext.registry.RESTRegistryService",
          "regex":"/registry(/.*)?",
          "path":"/registry/"
       },
       {
          "fqn":"org.exoplatform.services.jcr.webdav.WebDavServiceImpl",
          "regex":"/jcr(/.*)?",
          "path":"/jcr"
       },
       {
          "fqn":"org.exoplatform.services.rest.ext.service.RestServicesList",
          "regex":"(/.*)?",
          "path":"/"
       }
    ]}

## 4.4. Groovy Scripts as REST Services

This section describes how to use Groovy scripts as REST services. Consider these operations:

- Load script and save it in JCR.

- Instantiate script

- Deploy a newly created Class as RESTful service.

- Script Lifecycle Management.

- Finally, we will discover simple example which can get JCR node UUID

In this section, we consider RESTful service to be compatible with JSR-311 specification. The last feature is currently available in version 1.11-SNAPSHOT.

### 4.4.1. Loading script and save it in JCR

There are two ways to save a script in JCR. The first way is to save it at server startup time by using configuration.xml and the second way is to upload the script via HTTP.

Load script at startup time

This way can be used for load prepared scripts, to use this way. we must configure org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoaderPlugin. This is simple configuration example.

    <external-component-plugins>

      <target-component>org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoader</target-component>

      <component-plugin>

        <name>test</name>

        <set-method>addPlugin</set-method>

        <type>org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoaderPlugin</type>

        <init-params>

          <value-param>

            <name>repository</name>

            <value>repository</value>

          </value-param>

          <value-param>

            <name>workspace</name>

            <value>production</value>

          </value-param>

          <value-param>

            <name>node</name>

            <value>/script/groovy</value>

          </value-param>

          <properties-param>

            <name>JcrGroovyTest.groovy</name>

            <property name="autoload" value="true" />

            <property name="path" value="file:/home/andrew/JcrGroovyTest.groovy" />

          </properties-param>

        </init-params>

      </component-plugin>

    </external-component-plugins>

The first is value-param sets JCR repository, the second is value-param sets workspace and the third one is sets JCR node where scripts from plugin will be stored. If specified node does not exist, then it will be created. List of scripts is set by properties-params. Name of each properties-param will be used as node name for stored script, property autoload says to deploy this script at startup time, property path sets the source of script to be loaded. In this example we try to load single script from local file /home/andrew/JcrGroovyTest.groovy.

Load script via HTTP

This is samples of HTTP requests. In this example, we will upload script from file with name test.groovy.

    andrew@ossl:~> curl -u root:exo \
    -X POST \
    -H 'Content-type:script/groovy' \
    --data-binary @test.groovy \
    http://localhost:8080/rest/script/groovy/add/repository/production/script/groovy/test.groovy

This example imitate sending data with HTML form ('multipart/form-data'). Parameter autoload is optional. If parameter autoload=true then script will be instantiate and deploy script immediately.

    andrew@ossl:~> curl -u root:exo \
    -X POST \
    -F "file=@test.groovy;name=test" \
    -F "autoload=true" \
    http://localhost:8080/rest/script/groovy/add/repository/production/script/groovy/test1.groovy

### 4.4.2. Instantiation

org.exoplatform.services.script.groovy.GroovyScriptInstantiator is part of project exo.core.component.script.groovy. GroovyScriptInstantiator can load script from specified URL and parse stream that contains Groovy source code. It has possibility inject component from Container in Groovy Class constructor. Configuration example:

    <component>

      <type>org.exoplatform.services.script.groovy.GroovyScriptInstantiator</type>

    </component>

### 4.4.3. Deploying newly created Class as RESTful service

To deploy script automatically at server statup time, its property exo:autoload must be set as true. org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoader check JCR workspaces which were specified in configuration and deploy all auto-loadable scripts.

Example of configuration.

    <component>

        <type>org.exoplatform.services.jcr.ext.script.groovy.GroovyScript2RestLoader</type>

        <init-params>

          <object-param>

            <name>observation.config</name>

            <object type="org.exoplatform.services.jcr.ext.script.groovy.ObservationListenerConfiguration">

              <field name="repository">

                <string>repository</string>

              </field>

              <field name="workspaces">

                <collection type="java.util.ArrayList">

                  <value>

                    <string>production</string>

                  </value>

                </collection>

              </field>

            </object>

          </object-param>

        </init-params>

      </component>

In example above JCR workspace "production" will be checked for autoload scripts. At once, this workspace will be listened for changes script's source code (property jcr:data).

### 4.4.4. Script Lifecycle Management

If GroovyScript2RestLoader configured as was decribed in the previous section, then all "autoload" scripts deployed. In the first section, we added script from file /home/andrew/JcrGroovyTest.groovy to JCR node /script/groovy/JcrGroovyTest.groovy, repository repository, workspace production. In section "Load script via HTTP", it was refered about load scripts via HTTP, there is an opportunity to manage the life cycle of script.

Undeploy script, which is alredy deployed:

    andrew@ossl:~> curl -u root:exo \
    -X GET \
    http://localhost:8080/rest/script/groovy/load/repository/production/script/groovy/JcrGroovyTest.groovy?state=false

Then deploy it again:

    andrew@ossl:~> curl -u root:exo \
    -X GET \
    http://localhost:8080/rest/script/groovy/load/repository/production/script/groovy/JcrGroovyTest.groovy?state=true

or even more simple:

    andrew@ossl:~> curl -u root:exo \
    -X GET \
    http://localhost:8080/rest/script/groovy/load/repository/production/script/groovy/JcrGroovyTest.groovy

Disable scripts autoloading, NOTE it does not change current state:

    andrew@ossl:~> curl -u root:exo \
    -X GET \
    http://localhost:8080/rest/script/groovy/repository/production/script/groovy/JcrGroovyTest.groovy/autoload?state=false

Enable it again:

    andrew@ossl:~> curl -u root:exo \
    -X GET \
    http://localhost:8080/rest/script/groovy/autoload/repository/production/script/groovy/JcrGroovyTest.groovy?state=true

and again more simpe variant:

    andrew@ossl:~> curl -u root:exo \
    -X GET \
    http://localhost:8080/rest/script/groovy/autoload/repository/production/script/groovy/JcrGroovyTest.groovy

Change script source code:

    andrew@ossl:~> curl -u root:exo \
    -X POST \
    -H 'Content-type:script/groovy' \
    --data-binary @JcrGroovyTest.groovy \
    http://localhost:8080/rest/script/groovy/update/repository/production/script/groovy/JcrGroovyTest.groovy

This example imitates sending data with HTML form ('multipart/form-data').

    andrew@ossl:~> curl -u root:exo \
    -X POST \
    -F "file=@JcrGroovyTest.groovy;name=test" \
    http://localhost:8080/rest/script/groovy/update/repository/production/script/groovy/JcrGroovyTest.groovy

Remove script from JCR:

    andrew@ossl:~> curl -u root:exo \
    -X GET \
    http://localhost:8080/rest/script/groovy/delete/repository/production/script/groovy/JcrGroovyTest.groovy

### 4.4.5. Getting node UUID example

Now we are going to try simple example of Groovy RESTfull service. There is one limitation, even if we use groovy, we should use Java style code and decline to use dynamic types, but of course we can use it in private methods and feilds. Create file JcrGroovyTest.groovy, in this example I save it in my home directory /home/andrew/JcrGroovyTest.groovy. Then, configure GroovyScript2RestLoaderPlugin as described in section Load script at startup time.

    import javax.jcr.Node

    import javax.jcr.Session

    import javax.ws.rs.GET

    import javax.ws.rs.Path

    import javax.ws.rs.PathParam

    import org.exoplatform.services.jcr.RepositoryService

    import org.exoplatform.services.jcr.ext.app.ThreadLocalSessionProviderService

    @Path("groovy/test/{repository}/{workspace}")

    public class JcrGroovyTest {

      private RepositoryService                 repositoryService

      private ThreadLocalSessionProviderService sessionProviderService

      public JcrGroovyTest(RepositoryService repositoryService,

                           ThreadLocalSessionProviderService sessionProviderService) {

        this.repositoryService = repositoryService

        this.sessionProviderService = sessionProviderService

      }

      @GET

      @Path("{path:.*}")

      public String nodeUUID(@PathParam("repository") String repository,

                             @PathParam("workspace") String workspace,

                             @PathParam("path") String path) {

        Session ses = null

        try {

          ses = sessionProviderService.getSessionProvider(null).getSession(workspace, repositoryService.getRepository(repository))

          Node node = (Node) ses.getItem("/" + path)

          return node.getUUID() + "\n"

        } finally {

          if (ses != null)

            ses.logout()

        }

      }

After configuration is done, start the server. If configuration is correct and script does not have syntax error, you should see next:

In the screenshot, we can see the service deployed.

First, create a folder via WebDAV in the repository production, folder name 'test'. Now, we can try access this service. Open another console and type command:

    andrew@ossl:~> curl -u root:exo \
    http://localhost:8080/rest/groovy/test/repository/production/test

Whe you try to execute this command, you should have exception, because JCR node '/test' is not referenceable and has not UUID. We can try add mixin mix:referenceable. To do this, add one more method in script. Open script from local source code /home/andrew/JcrGroovyTest.groovy, add following code and save file.

    @POST

    @Path("{path:.*}")

    public void addReferenceableMixin(@PathParam("repository") String repository,

                                      @PathParam("workspace") String workspace,

                                      @PathParam("path") String path) {

      Session ses = null

      try {

        ses = sessionProviderService.getSessionProvider(null).getSession(workspace, repositoryService.getRepository(repository))

        Node node = (Node) ses.getItem("/" + path)

        node.addMixin("mix:referenceable")

        ses.save()

      } finally {

        if (ses != null)

          ses.logout()

      }

    }

Now we can try to change script deployed on the server without server restart. Type in console next command:

    andrew@ossl:~> curl -i -v -u root:exo \
    -X POST \
    --data-binary @JcrGroovyTest.groovy \
    -H 'Content-type:script/groovy' \
    http://localhost:8080/rest/script/groovy/update/repository/production/script/groovy/JcrGroovyTest.groovy

Node '/script/groovy/JcrGroovyTest.groovy' has property exo:autoload=true so script will be re-deployed automatically when script source code changed.

Script was redeployed, now try to access a newly created method.

    andrew@ossl:~> curl -u root:exo \
    -X POST \
    http://localhost:8080/rest/groovy/test/repository/production/test

Method excution should be quiet, without output, traces, etc. Then we can try again get node UUID.

    andrew@ossl:~> curl -u root:exo \
    http://localhost:8080/rest/groovy/test/repository/production/test
    1b8c88d37f0000020084433d3af4941f

Node UUID: 1b8c88d37f0000020084433d3af4941f

We don't need this scripts any more, so remove it from JCR.

    andrew@ossl:~> curl -u root:exo \
    http://localhost:8080/rest/script/groovy/delete/repository/production/script/groovy/JcrGroovyTest.groovy

### 4.4.6. Groovy script restrictions

You should keep one class per one groovy file. The same actually for interface and it implementation. It's limitation of groovy parser that does not have type Class[] parseClass(InputStream) or Collection parseClass(InputStream) but only Class parseClass(InputStream) instead.

That is all.

## 4.5. Framework for cross-domain AJAX

eXo Webservice provides a framework to cross-domain AJAX. This section shows you how to use this framework.

You can checkout the source code at https://anonsvn.jboss.org/repos/exo-jcr/ws/trunk/exo.ws.frameworks.javascript.cross-domain-ajax.

### 4.5.1. Motivation

XmlHttpRequest objects are bound by the same origin security policy of browsers, which prevents a page from accessing data from another server. This has put a serious limitation on Ajax developers: you can use XmlHttpRequests to make background calls to a server, but it has to be the same server that served up the current page. For more details, you can visit http://www.mozilla.org/projects/security/components/same-origin.html.

But actually writing client web applications that use this object can be tricky given restrictions imposed by web browsers on network connections across domains. So you need to find the way to bypass this limitation of AJAX.

### 4.5.2. Scheme (how it works)

To describe our method for cross-domain AJAX solution, let's consider the following scheme contains of 3 components:

1). User agent (a browser).

2). ServerA contains a main page with dedicated client and server IFRAMEs (see below) and an HTML client page (client.html) referenced from the client IFRAME. This client page contains dedicated script to push the data for request into server IFRAME.

3). ServerB contains remote service that want get access to and an HTML server page (server.html) referenced from the server IFRAME. This server page contains dedicated script to push the requested data into client IFRAME.

### 4.5.3. A Working Sequence:

1) A Browser requests the Start page from the ServerA

2) The Start page is retrieved from the ServerA.

3) Create in the start page IFRAME (name it - "client iframe") and insert it in the document from ServerA (client.

4) In "client iframe" create ne IFRAME element ("server iframe") and insert it in the document from ServerB (server.html). Documents (client.html and server.html) contain special script that can transfer data between ifarmes.

5) "Client iframe" transfer information about HTTP method and URL that we want do cross-domain request to "server iframe".

6) "Server iframe" do simple XmlHttpRequest to the service that we need (it can do that because download from same domain) and get informaton from service.

7) "Server iframe" transfer data to "client iframe" and now we get information that we want.

### 4.5.4. How to use it

1). Place the file client.html and xda.js on the serverA.

2). Place the file server.html on the serverB.

3). Declare xda.js in the main page.

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

4). Create JS function which performs cross domain call as in the following example:

    <script type="text/javascript">
      function test(){
                    var facade = xdaInit();
                    facade.clientURI = "http://localhost/cross-domain-ajax/client/client.html";
                    facade.serverURI = "http://localhost:8080/cross-domain-ajax/server/server.html";
                    facade.apiURI = "http://localhost:8080/cross-domain-ajax/server/test.txt";
                    facade.method = "POST";
                    facade.load = function(result) {
                                    alert(result.responseText);
                    }
                    facade.setRequestHeader("keep-alive","200");
                    xda.create(facade);
            }
    </script>

5). Use this function (here it is bound to a button's onclick event).

    <button onclick='test()'>test cross-domain</button>

Copyright © 2009-2012. All rights reserved. eXo Platform SAS