• ALL
  • CATEGORIES

Using the WebCenter Sites REST API – Part 1

This tutorial is a two part introduction to the WebCenter Sites REST API.

In this, the first part, we’ll look at building a simple java client for communicating with WebCenter Sites. In the second we’ll use that client to manage users in WCS by storing their details in a spreadsheet.

Getting started

To start with you’ll need the following installed:

  • A local instance of WCS (this could be a JSK or similar)
  • Eclipse
  • Maven

Now you can either clone this project from GitHub or you can follow along with the tutorial and create the project yourself. First lets look at our POM

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>uk.co.manifesto.wcs</groupId>
  <artifactId>data-manager</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>data-manager-part1</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <!-- jersey -->
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.17.1</version>
    </dependency>
    <!-- sites -->
    <dependency>
        <groupId>com.fatwire.wem.sso.cas</groupId>
        <artifactId>wem-sso-api-cas</artifactId>
        <version>11.1.1.8.0</version>
    </dependency>
    <dependency>
        <groupId>com.fatwire.wem.sso</groupId>
        <artifactId>wem-sso-api</artifactId>
        <version>11.1.1.8.0</version>
    </dependency>   
    <dependency>
        <groupId>com.fatwire.wem.api</groupId>
        <artifactId>rest-api</artifactId>
        <version>11.1.1.8.0</version>
    </dependency>
    <!-- spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>3.2.4.RELEASE</version>
    </dependency>   
    <!-- guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>15.0</version>
    </dependency>   
    <!-- cas -->
    <dependency>
        <groupId>org.jasig.cas</groupId>
        <artifactId>cas-client-core</artifactId>
        <version>3.1.9</version>
    </dependency>           
    <!-- test -->              
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build> 
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.0.2</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
    </plugins>
  </build>
</project>

We’ve got some WCS dependencies that won’t be in Maven Central so lets go and add them to our local repository.

The easiest way to do this to navigate to the lib directory of the cs web application on the command line and run the following mvn commands

mvn install:install-file -Dfile=wem-sso-api-cas-11.1.1.8.0.jar -DgroupId=com.fatwire.wem.sso.cas -DartifactId=wem-sso-api-cas -Dversion=11.1.1.8.0 -Dpackaging=jar -DgeneratePom=true

mvn install:install-file -Dfile=wem-sso-api-11.1.1.8.0.jar -DgroupId=com.fatwire.wem.sso -DartifactId=wem-sso-api -Dversion=11.1.1.8.0 -Dpackaging=jar -DgeneratePom=true

mvn install:install-file -Dfile=rest-api-11.1.1.8.0.jar -DgroupId=com.fatwire.wem.api -DartifactId=rest-api -Dversion=11.1.1.8.0 -Dpackaging=jar -DgeneratePom=true

Creating the Client

To start with we’re going to use the Client class that Jersey provides us to do the heavy lifting.

We’re then going to create a method for authenticating with WebCenter Sites.

Finally we’ll create some generic methods wrapping the basic Create, Read, Update and Delete functionality provided by the underlying API.

Let’s start by having a look at the constructor and fields for our RestClient class:

    private Client client;
    private String baseUri;
    private String multiticket;
    private WebResource baseResource;
    private boolean connected = false;
    private final String username;
    private final String password;

public RestClient(String baseUri, String username, String password) throws RestConnectionException {
    this.client = new Client();
    this.baseUri = baseUri;
    this.baseResource = client.resource(baseUri + "REST");
    this.username = username;
    this.password = password;
    authenticate();
}

Passed in to the constructor are the baseUri for the WebCenter Rest API and then a username and password.

This allows us to authentication with the service.

Then we create a Jersey client to use and set our baseResource to the REST endpoint.

Authentication

Let’s have a look at the authenticate method next:

private void authenticate() throws RestConnectionException {
    try {
        SSOSession ssoSession = SSO.getSSOSession(baseUri);
        multiticket = ssoSession.getMultiTicket(username, password);
        baseResource = baseResource.queryParam("multiticket", multiticket);
    } catch (SSOException e) {
        throw new RestConnectionException(e);
    }
} 

There are a couple of ways to authenticate with WebCenter Sites but I think this way is the easiest – it relies on passing the baseUri of our WebCenter Sites instance to the static method getSSOSession of the SSO class.

This then allows to get a multiticket which we add as a query parameter to our baseResource.

It’s worth noting at this point that a multiticket allows us to reuse the query parameter for multiple request, it does however eventually expire and a more complete implementation would need to provide a strategy for catching that exception and re-attempting authentication.

It’s also worth talking briefly about the way we’re using getSSOSession. The javadoc for this class isn’t particularly clear that it is possible to use the method in the way that we do. It says the method

Obtains a new SSO session by loading configuration from a Spring configuration file.

What it doesn’t tell you is that you can find that config for a particular WebCenter Sites instance by passing in the baseUri.

The URI that’s ultimately queried for the information is this

/cs/Satellite?pagename=fatwire/wem/sso/casInfo

and if you have a look at the response you can see it returns an XML document that looks like this

<casproperties>
<authPrefix>cas</authPrefix>
<restServiceUrl>http://localhost:9080/cas</restServiceUrl>
<ticketServerUrl>http://localhost:9080/cas/v1/tickets</ticketServerUrl>
<ticketParamName>ticket</ticketParamName>
<multiticketParamName>multiticket</multiticketParamName>
<usesso>false</usesso>
<providerClass>com.fatwire.wem.sso.cas.CASProvider</providerClass>
<configClass>com.fatwire.wem.sso.cas.conf.CASConfig</configClass>
</casproperties>

If for whatever reason you’re uncomfortable using this method to authenticate you can follow the instructions in the Oracle documentation. They are a little longer winded but perform the same function.

Now that we’ve authenticated we can start talking CRUD.

CRUD

The next step in building our client is to provide methods to Create, Read, Update and Delete resources from WCS.

Lets take a look at our Get (Read) method first

public ClientResponse get(String path)  {
    WebResource getResource = this.baseResource.path(getRelativePath(path));    
    Builder builder = getResource.accept(MediaType.APPLICATION_XML);
    return builder.get(ClientResponse.class);
}

This is a fairly simple method in that it accepts a path either relative to the baseResource (/cs/REST) or an absolute URL.

We use this path to create a WebResource and then use the WebResource.Builder class to create a HTTP GET request to our resource and return the response wrapped in a ClientResponse class.

The ClientResponse class allows us to interrogate the response before we decide what we want to do with it.

For example this code snippets checks to see that a request for users has been successful before trying to serialise it.

    ClientResponse response = client.get("/users");
    if (response.getStatus() == 200) {
        UsersBean users = response.getEntity(UsersBean.class);
    }  

Next let’s look at our Create method and its related helper method getBuilder

public <T> ClientResponse post(T bean, String path) {
    WebResource getResource = this.baseResource.path(getRelativePath(path));
    Builder builder = getBuilder(getResource);
    return builder.post(ClientResponse.class, bean);
}

private Builder getBuilder(WebResource getResource) {
    Builder builder = getResource.accept(MediaType.APPLICATION_XML);
    builder = builder.header("Pragma", "auth-redirect=false");
    builder = builder.header("X-CSRF-Token", multiticket);
    return builder;
}

Again we accept a path identifying the resource that we want to post to, but this time, using Generics, we also accept a javabean for posting to the server.

This Oracle documentation describes the list of the beans available.

There are a couple of things interesting in the getBuilder helper method, the first is the X-CSRF-Token header.

Without adding this token we won’t be able to perform any of the PUT,POST,DELETE methods – you’ll notice that it’s just our multiticket value.

And the second Pragma header lets the underlying service know that we’re making this request outside of a web application so that if authentication fails we don’t want to be redirected to the login page.

Lastly our Update and Delete methods are pretty similar although delete doesn’t return require a java bean in the request body

public ClientResponse delete(String path) {
    WebResource getResource = this.baseResource.path(getRelativePath(path));
    Builder builder = getBuilder(getResource);
    return builder.delete(ClientResponse.class);
}

public <T> ClientResponse put(T bean, String path) {
    WebResource getResource = this.baseResource.path(getRelativePath(path));
    Builder builder = getBuilder(getResource);
    return builder.put(ClientResponse.class, bean);
}

With all this in place our final RestClient class looks like this

package uk.co.manifesto.wcs.datamanager.rest;

import javax.ws.rs.core.MediaType;

import uk.co.manifesto.wcs.datamanager.rest.exception.RestConnectionException;

import com.fatwire.wem.sso.SSO;
import com.fatwire.wem.sso.SSOException;
import com.fatwire.wem.sso.SSOSession;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.WebResource.Builder;

public class RestClient {

    private Client client;
    private String baseUri;
    private String multiticket;
    private WebResource baseResource;
    private final String username;
    private final String password;

    public RestClient(String baseUri, String username, String password) throws RestConnectionException {
        this.client = new Client();
        this.baseUri = baseUri;
        this.baseResource = client.resource(baseUri + "REST");
        this.username = username;
        this.password = password;
        authenticate();
    }

    public ClientResponse get(String path)  {
        WebResource getResource = this.baseResource.path(getRelativePath(path));    
        Builder builder = getResource.accept(MediaType.APPLICATION_XML);
        return builder.get(ClientResponse.class);
    }

    public ClientResponse delete(String path) {
        WebResource getResource = this.baseResource.path(getRelativePath(path));
        Builder builder = getBuilder(getResource);
        return builder.delete(ClientResponse.class);
    }

    public <T> ClientResponse put(T bean, String path) {
        WebResource getResource = this.baseResource.path(getRelativePath(path));
        Builder builder = getBuilder(getResource);
        return builder.put(ClientResponse.class, bean);
    }

    public <T> ClientResponse post(T bean, String path) {
        WebResource getResource = this.baseResource.path(getRelativePath(path));
        Builder builder = getBuilder(getResource);
        return builder.post(ClientResponse.class, bean);
    }

    private Builder getBuilder(WebResource getResource) {
        Builder builder = getResource.accept(MediaType.APPLICATION_XML);
        builder = builder.header("Pragma", "auth-redirect=false");
        builder = builder.header("X-CSRF-Token", multiticket);
        return builder;
    }

    private String getRelativePath(String path) {
        if (path.startsWith(baseUri)) {
            path = path.replace(baseUri + "REST", "");
        }
        return path;
    }   

    private void authenticate() throws RestConnectionException {
        try {
            SSOSession ssoSession = SSO.getSSOSession(baseUri);
            multiticket = ssoSession.getMultiTicket(username, password);
            baseResource = baseResource.queryParam("multiticket", multiticket);
        } catch (SSOException e) {
            throw new RestConnectionException(e);
        }
    }
}

Sample Implementation

I’ve also included in the project a sample Runner class that exercises our RestClient.

The main method is shown below

    public static void main(String[] args) throws RestConnectionException {
        RestClient client = new RestClient("http://localhost:9080/cs/", "fwadmin","xceladmin");
        ClientResponse response = client.get("/users");
        if (response.getStatus() == 200) {
            UsersBean users = response.getEntity(UsersBean.class);
            for (User user : users.getUsers()) {
                ClientResponse userResponse = client.get(user.getHref());
                if (userResponse.getStatus() == 200) {
                    UserBean userBean = userResponse.getEntity(UserBean.class);
                    System.out.println(userBean.getName());
                    System.out.println(userBean.getId());
                    List<String> siteNames = new ArrayList<String>();
                    for (UserSite userSite : userBean.getSites()) {
                        siteNames.add(userSite.getSite() + "=>" + Joiner.on(",").join(userSite.getRoles()));
                    }
                    System.out.println(Joiner.on(" || ").join(siteNames));
                    System.out.println(Joiner.on(",").join(userBean.getAcls()));
                }
            }
        }
    }

All this does is get a list of users from WebCenter Sites and output their details.

Have a well earned rest (no pun intended) and I’ll see you in part two.

Leave a reply

You can use these tags:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

  1. Mohammad Hosafy says:

    Thank you so much, extremely helpful 🙂

  2. Srinivasan says:

    This should have been part of Oracle’s knowledge base. Well put and deserves an applaud. Thanks for saving our time. 🙂

  3. Jim says:

    Could the REST API be used to consume content from an external application (e.g. portal)? In a similar to which SSXA enables consumption of content from Webcenter Content?

  4. NN says:

    Why is not possible do this with spring framework in version 4?

  5. mau says:

    Hi,
    where can i find jars? And are they compatible with spring 4.0.x?
    thanks in advance!
    Mau

  6. Vanesa says:

    I cannot find jars for spring 4 but I solved the problem using xml configuration as in this Oracle Forum answer.

  7. Gio says:

    I’m trying to connect to my WCS 12c installation, but I obtain this error:

    Exception in thread “main” uk.co.manifesto.wcs.datamanager.rest.exception.RestConnectionException: com.fatwire.wem.sso.SSOException: casInfo: ticketServerUrl is missing
    at uk.co.manifesto.wcs.datamanager.rest.RestClient.authenticate(RestClient.java:78)
    at uk.co.manifesto.wcs.datamanager.rest.RestClient.(RestClient.java:31)
    at uk.co.manifesto.wcs.datamanager.rest.sample.RestClientRunner.main(RestClientRunner.java:19)
    Caused by: com.fatwire.wem.sso.SSOException: casInfo: ticketServerUrl is missing
    at com.fatwire.wem.sso.DynamicInjector.createBeanFactory(DynamicInjector.java:157)
    at com.fatwire.wem.sso.DynamicInjector.(DynamicInjector.java:76)
    at com.fatwire.wem.sso.DynamicInjector.instance(DynamicInjector.java:250)
    at com.fatwire.wem.sso.SSOSession.getSSOProvider(SSOSession.java:265)
    at com.fatwire.wem.sso.SSOSession.getMultiTicket(SSOSession.java:155)
    at uk.co.manifesto.wcs.datamanager.rest.RestClient.authenticate(RestClient.java:75)
    … 2 more

Sign up for the Manifesto newsletter and exclusive event invites