restfatwirewebcenter-sites

Webcenter Sites REST API Asset Creation using POST & PUT Exceptions, Response Status 401


Intention is to create an asset using java code and the Sites REST API.
First of I couldn't find the REST API samples anywhere in the installation or even source directories of the 11.1.1.8.0 Sites or JSK. Instead I found the the samples in the 11.1.1.6.0 server.

The code I'm running after changing the base URL in both the POSt and PUT methods:

/*
 * $Logfile:$ $Revision:$ $Date:$ Copyright © 2010 FatWire Corporation, All
 * Rights Reserved. Copyright 2010 FatWire Corporation. Title, ownership rights,
 * and intellectual property rights in and to this software remain with FatWire
 * Corporation. This software is protected by international copyright laws and
 * treaties, and may be protected by other law. Violation of copyright laws may
 * result in civil liability and criminal penalties.
 */
package com.fatwire.rest.samples.flex;

import javax.ws.rs.core.MediaType;

import com.fatwire.rest.beans.AssetBean;
import com.fatwire.rest.beans.Attribute;
import com.fatwire.rest.beans.Attribute.Data;
import com.fatwire.rest.beans.ErrorBean;
import com.fatwire.rest.beans.Parent;
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.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.WebResource.Builder;
import com.fatwire.wem.sso.cas.conf.CASConfig;

/**
 * <p>
 * Sample JAVA class to demonstrate creation of new Flex assets using REST API
 * exposed by Fatwire WEM module.
 * </p>
 * <p>
 * For this class to function, the following are the pre-requisites:
 * <ol>
 * <li>The following jars are required in the classpath:
 * <ul>
 * <li>rest-api.jar</li>
 * <li>wem-sso-api.jar</li>
 * <li>jersey-client.jar</li>
 * <li>jsr311-api.jar</li>
 * <li>jersey-client.jar</li>
 * <li>jersey-core.jar</li>
 * </ul>
 * </li>
 * <li>The following configuration file is required in the classpath (in the
 * example, the above file is referred to as <tt>ExampleCASConfig.xml</tt>.):
 * 
 * <pre>
 * &lt;?xml version="1.0" encoding="UTF-8"?&gt;
 * &lt;beans
 *      xmlns="http://www.springframework.org/schema/beans"
 *      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 *      xmlns:context="http://www.springframework.org/schema/context"
 *      xsi:schemaLocation="http://www.springframework.org/schema/beans
 *                          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 *                          http://www.springframework.org/schema/context
 *                          http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;
 *   &lt;!-- DONOT CHANGE: Single Sign On provider --&gt;
 *   &lt;bean id="ssoprovider" class="com.fatwire.wem.sso.cas.CASProvider"&gt;
 *     &lt;property name="config" ref="ssoconfig" /&gt;
 *   &lt;/bean&gt;
 *   &lt;!--
 *      Single Sign On configuration: change ONLY the following property:
 *          casUrl - URL of the CAS installation
 *   --&gt;
 *   &lt;bean id="ssoconfig" class="com.fatwire.wem.sso.cas.conf.CASConfig"&gt;
 *     &lt;property name="casUrl" value="${cas-url}" /&gt;
 *     &lt;property name="casRestPath" value="/v1" /&gt;
 *   &lt;/bean&gt;
 * &lt;/beans&gt;
 * </pre>
 * 
 * </li>
 * <li>The Content Server Installation must have the "FirstSiteII" sample site
 * and the "Product_C" asset type. "Product_C" should be enabled in
 * "FirstSiteII". There should not be an asset by the name of "Test Product_C"
 * which is the name of the test asset.</li>
 * </ol>
 * 
 * @author Saikat Chaudhuri
 */
public final class CreateAsset
{

    /**
     * Main method. Run this method to create a basic asset using:
     * <ul>
     * <li>{@link #createUsingPut()}</li>
     * <li>{@link #createUsingPost()}</li>
     * </ul>
     * 
     * @param args
     */
    public static void main(String[] args)
    {
        createUsingPut();
       // createUsingPost();
    }



    /**
     * Run this method to create a basic asset using PUT.
     */
    public static void createUsingPut()
    {
        // Step 1: Initiate Jersey client
        Client client = Client.create();

        // Step 2: Create a WebResource with the base URL
        WebResource webResource =
            client.resource("http://127.0.0.1:9080/cs/REST");

        // Step 3: Authenticate over SSO-CAS to acquire a ticket specific to a
        // service or a multi-ticket over multiple services.
        SSOSession ssoSession = null;
        String multiticket = null;
        try
        {
            ssoSession = SSO.getSSOSession("ExampleCASConfig.xml");
            multiticket = ssoSession.getMultiTicket("fwadmin", "xceladmin");
        }
        catch (SSOException e)
        {
            // Troubleshooting SSOException
            // ============================
            //
            // Cause: CAS is not running.
            // Remedy: Deploy CAS and start up the application server for CAS
            // webapp.
            //
            // Cause: CAS is not configured.
            // Remedy: Verify that the URL in the CAS config file,
            // ExampleCASConfig.xml, is reachable.
            //
            // Cause: Username / password is invalid.
            // Remedy: Use valid username / password to authenticate against
            // CAS.

            // Handle exception
            e.printStackTrace();
        }

        // Step 4: Provide the ticket into the REST request
        webResource = webResource.queryParam("multiticket", multiticket);

        // Trying to create a Flex asset for the Flex asset type
        // "Product_C" in the CS site "FirstSiteII"
        String flexAssetSiteName = "FirstSiteII";
        String flexAssetTypeName = "Product_C";

        // Step 5: Specify the REST Resource URL into the WebResource
        // For creating assets of type {typename} in the CS site {sitename},
        // this is: {base_url}/sites/{sitename}/types/{typename}/assets/0
        webResource =
            webResource.path("sites").path(flexAssetSiteName).path("types")
                .path(flexAssetTypeName).path("assets").path("0");

        // Step 6: Create a Builder and set the desired response type
        // Supported response types are:
        // MediaType.APPLICATION_XML, or,
        // MediaType.APPLICATION_JSON
        Builder builder = webResource.accept(MediaType.APPLICATION_XML);

        // Step 7: Instantiate and define the AssetBean for the asset
        AssetBean sourceAsset = new AssetBean();

        // Name - mandatory field
        sourceAsset.setName("Test Product_C PUT");

        // Description - optional field
        sourceAsset.setDescription("Test Product_C PUT description");

        // Add attributes / associations / parents as in the Asset type
        // definition
        Attribute sourceAssetAttribute = new Attribute();
        Data sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIISKU");
        sourceAssetAttributeData.setStringValue("Test SKU");
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        sourceAssetAttribute = new Attribute();
        sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIILongDescription");
        sourceAssetAttributeData.setStringValue("Test Long Description");
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        sourceAssetAttribute = new Attribute();
        sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIIPrice");
        sourceAssetAttributeData.setDoubleValue(10.00);
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        sourceAssetAttribute = new Attribute();
        sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIIImage");
        sourceAssetAttributeData.setStringValue("Media_C:1114083738411");
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        Parent parent = new Parent();
        parent.setParentDefName("FSIIManufacturer");
        parent.getAssets().add("Product_P:1114083739006");
        sourceAsset.getParents().add(parent);

        parent = new Parent();
        parent.setParentDefName("FSIISubcategory");
        parent.getAssets().add("Product_P:1114083739104");
        sourceAsset.getParents().add(parent);

        // Required: Must specify the site(s) for the asset
        sourceAsset.getPublists().add(flexAssetSiteName);

        // Required: Must specify the sub type for the Flex asset type
        sourceAsset.setSubtype("FSII Product");

        // Step 8: Invoke the REST request to create the asset
        AssetBean resultAsset = builder.put(AssetBean.class, sourceAsset);

        // If the REST call encounter a server side exception, the client
        // receives a UniformInterfaceException at runtime.


        // Troubleshooting UniformInterfaceException
        // =========================================
        //
        // Cause: HTTP 403: User does not have permission to access the REST
        // resource.
        // Remedy: Use an authorized CAS user to use this REST resource.
        //
        // Cause: HTTP 404: Either site and/or asset type does not exist, or the
        // asset type is not enabled in the site.
        // Remedy: Verify existence of the site and/or asset type, if necessary
        // create the site and/or type and make sure that the type is enabled in
        // the site.
        //
        // Cause: HTTP 500: Generic server side exception.
        // Remedy: Verify that the source AssetBean has been provided with all
        // mandatory attributes, associations as per type definition, verify
        // that at least one site has been provided in the publist attribute of
        // the AssetBean.
        //
        // Cause: UnmarshalException.
        // Remedy: Verify that the correct bean has been provided in the
        // argument for put().
    }

    /**
     * Run this method to create a basic asset using POST.
     */
    public static void createUsingPost()
    {

        // Step 1: Initiate Jersey client
        Client client = Client.create();

        // Step 2: Create a WebResource with the base URL
        WebResource webResource =
            client.resource("http://127.0.0.1:9080/cs/REST");

        // Step 3: Authenticate over SSO-CAS to acquire a ticket specific to a
        // service or a multi-ticket over multiple services.
        SSOSession ssoSession = null;
        String multiticket = null;
        try
        {
            ssoSession = SSO.getSSOSession("ExampleCASConfig.xml");
            multiticket = ssoSession.getMultiTicket("fwadmin", "xceladmin");
        }
        catch (SSOException e)
        {
            // Troubleshooting SSOException
            // ============================
            //
            // Cause: CAS is not running.
            // Remedy: Deploy CAS and start up the application server for CAS
            // webapp.
            //
            // Cause: CAS is not configured.
            // Remedy: Verify that the URL in the CAS config file,
            // ExampleCASConfig.xml, is reachable.
            //
            // Cause: Username / password is invalid.
            // Remedy: Use valid username / password to authenticate against
            // CAS.

            // Handle exception
            e.printStackTrace();
        }

        // Step 4: Provide the ticket into the REST request
        webResource = webResource.queryParam("multiticket", multiticket);

        // Trying to create a Flex asset for the Flex asset type
        // "Product_C" in the CS site "FirstSiteII"
        String flexAssetSiteName = "FirstSiteII";
        String flexAssetTypeName = "Product_C";

        // Step 5: Specify the REST Resource URL into the WebResource
        // For creating assets of type {typename} in the CS site {sitename},
        // this is: {base_url}/sites/{sitename}/types/{typename}/assets
        webResource =
            webResource.path("sites").path(flexAssetSiteName).path("types")
                .path(flexAssetTypeName).path("assets");

        // Step 6: Create a Builder and set the desired response type
        // Supported response types are:
        // MediaType.APPLICATION_XML, or,
        // MediaType.APPLICATION_JSON
        Builder builder = webResource.accept(MediaType.APPLICATION_XML);

        // Step 7: Instantiate and define the AssetBean for the asset
        AssetBean sourceAsset = new AssetBean();

        // Name - mandatory field
        sourceAsset.setName("Test Product_C POST");

        // Description - optional field
        sourceAsset.setDescription("Test Product_C POST description");

        // Add attributes / associations / parents as in the Asset type
        // definition
        Attribute sourceAssetAttribute = new Attribute();
        Data sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIISKU");
        sourceAssetAttributeData.setStringValue("Test SKU");
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        sourceAssetAttribute = new Attribute();
        sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIILongDescription");
        sourceAssetAttributeData.setStringValue("Test Long Description");
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        sourceAssetAttribute = new Attribute();
        sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIIPrice");
        sourceAssetAttributeData.setDoubleValue(10.00);
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        sourceAssetAttribute = new Attribute();
        sourceAssetAttributeData = new Data();
        sourceAssetAttribute.setName("FSIIImage");
        sourceAssetAttributeData.setStringValue("Media_C:1114083738411");
        sourceAssetAttribute.setData(sourceAssetAttributeData);
        sourceAsset.getAttributes().add(sourceAssetAttribute);

        Parent parent = new Parent();
        parent.setParentDefName("FSIIManufacturer");
        parent.getAssets().add("Product_P:1114083739006");
        sourceAsset.getParents().add(parent);

        parent = new Parent();
        parent.setParentDefName("FSIISubcategory");
        parent.getAssets().add("Product_P:1114083739104");
        sourceAsset.getParents().add(parent);

        // Required: Must specify the site(s) for the asset
        sourceAsset.getPublists().add(flexAssetSiteName);

        // Required: Must specify the sub type for the Flex asset type
        sourceAsset.setSubtype("FSII Product");

        // Step 8: Invoke the REST request to create the asset
        ClientResponse resp = builder.post(ClientResponse.class, sourceAsset);

        // The "Location" header provides the element URI
        // which can be used to read back the asset which was created.
        String elementURI = resp.getHeaders().get("Location").get(0);

        // If the REST call encounter a server side exception, the client
        // receives a UniformInterfaceException at runtime.

        // Troubleshooting UniformInterfaceException
        // =========================================
        //
        // Cause: HTTP 403: User does not have permission to access the REST
        // resource.
        // Remedy: Use an authorized CAS user to use this REST resource.
        //
        // Cause: HTTP 404: Either site and/or asset type does not exist, or the
        // asset type is not enabled in the site.
        // Remedy: Verify existence of the site and/or asset type, if necessary
        // create the site and/or type and make sure that the type is enabled in
        // the site.
        //
        // Cause: HTTP 500: Generic server side exception.
        // Remedy: Verify that the source AssetBean has been provided with all
        // mandatory attributes, associations as per type definition, verify
        // that at least one site has been provided in the publist attribute of
        // the AssetBean.
        //
        // Cause: UnmarshalException.
        // Remedy: Verify that the correct bean has been provided in the
        // argument for post().
    }
}

Adding the JARs I searched for every JAR name mentioned in the sample comments in the the JSK Installation directory and added them.

The JARs:

Apparently these JARs we needed also but not mentioned:

From the source code comments I extracted the XML file to be used as spring configuration, in the ssoconfig bean added the casUrl value, and changed the attribute name of casRestPath to restPath as it didn't exist in the class.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                          http://www.springframework.org/schema/context
                          http://www.springframework.org/schema/context/spring-context-2.5.xsd">


    <!-- DONOT CHANGE: Single Sign On provider -->
    <bean id="ssoprovider" class="com.fatwire.wem.sso.cas.CASProvider">
        <property name="config" ref="ssoconfig"/>
    </bean>



    <!-- Single Sign On configuration: change ONLY the following property: casUrl 
        - URL of the CAS installation -->
    <bean id="ssoconfig" class="com.fatwire.wem.sso.cas.conf.CASConfig">
        <property name="casUrl" value="http://127.0.0.1:9080/cas" />
        <property name="restPath" value="/v1" />
    </bean>


</beans>

The Exception I get running the POST method:

Jan 08, 2014 5:43:50 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4c08964: startup date [Wed Jan 08 17:43:50 GMT+02:00 2014]; root of context hierarchy
Jan 08, 2014 5:43:50 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ExampleCASConfig.xml]
Jan 08, 2014 5:43:50 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3c200d0: defining beans [ssoprovider,ssoconfig]; root of factory hierarchy
Exception in thread "main" com.sun.jersey.api.client.UniformInterfaceException: PUT http://127.0.0.1:9080/cs/REST/sites/FirstSiteII/types/Product_C/assets/0?multiticket=multi-ST-39-DPgghOvnBCAAggKLAHpa-_tmp_1374099243365 returned a response status of 401
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:563)
    at com.sun.jersey.api.client.WebResource.access$300(WebResource.java:69)
    at com.sun.jersey.api.client.WebResource$Builder.put(WebResource.java:475)
    at com.fatwire.rest.samples.flex.CreateAsset.createUsingPut(CreateAsset.java:222)
    at com.fatwire.rest.samples.flex.CreateAsset.main(CreateAsset.java:97)

On debugging I found out the the response came back with status 401 .

The Exception I get running the PUT method:

Jan 09, 2014 12:03:36 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4c08964: startup date [Thu Jan 09 12:03:36 GMT+02:00 2014]; root of context hierarchy
Jan 09, 2014 12:03:36 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ExampleCASConfig.xml]
Jan 09, 2014 12:03:36 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3c200d0: defining beans [ssoprovider,ssoconfig]; root of factory hierarchy
Exception in thread "main" com.sun.jersey.api.client.UniformInterfaceException: PUT http://127.0.0.1:9080/cs/REST/sites/FirstSiteII/types/Product_C/assets/0?multiticket=multi-ST-7-kejMYvRWO1cua39MfPYn-_tmp_1374099243365 returned a response status of 401
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:563)
    at com.sun.jersey.api.client.WebResource.access$300(WebResource.java:69)
    at com.sun.jersey.api.client.WebResource$Builder.put(WebResource.java:475)
    at com.fatwire.rest.samples.flex.CreateAsset.createUsingPut(CreateAsset.java:222)
    at com.fatwire.rest.samples.flex.CreateAsset.main(CreateAsset.java:97)

There are no existing assets with the same name as the one in the code. The fwadmin user is tested but the default configuration of the JSK and also the after adding it to all security groups and roles (including REST ofcourse). All used asset ID/s in the sample (for the parents and so) check out, they exist.


Solution

  • Don't know about the new code (11.1.1.8.0) but this old (11.1.1.6.0) code used doesn't cope with the CSRF protection token and the new version of Sites added a CSRF protection filter, so we can either handle the token from the code (as used in the new code) :

        //Add the CSRF header (between steps 6 & 7).
        builder = builder.header("X-CSRF-Token", multiticket);
    

    The 11.1.1.8.0 code location

    WCS_Sites/misc/Samples/WEM Samples/REST API samples/Flex Assets/com/fatwire/rest

    or add the rest request patterns to the filter exceptions list in ReqAuthConfig.xml file in /WEB-INF/classes folder

        <property name="excludedURLs">
            <!--  URLs in this section would be excluded from CSRF protection-->
            <!--  For example to exclude rest calls add <value>/REST/**</value> in the list-->
           <list>
            <value>/ContentServer?[pagename=fatwire/wem/sso/ssoLogin|OpenMarket/Xcelerate/Actions/ProcessQueue|OpenMarket/Xcelerate/Actions/CleanTempObjects|OpenMarket/Xcelerate/Search/Event,#]</value>   
            <value>/CatalogManager?[ftcmd=login,#]</value>
            <value>/FlushServer</value>
            <value>/CacheServer</value>
            <value>/cachetool/inventory.jsp</value>
            <value>/Inventory</value>
          </list>
        </property>