org.betaconceptframework.astroboa.resourceapi.resource.ContentObjectResource.java Source code

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.resourceapi.resource.ContentObjectResource.java

Source

/*
 * Copyright (C) 2005-2012 BetaCONCEPT Limited
 *
 * This file is part of Astroboa.
 *
 * Astroboa is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Astroboa is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Astroboa.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.betaconceptframework.astroboa.resourceapi.resource;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DurationFormatUtils;
import org.betaconceptframework.astroboa.api.model.BinaryChannel.ContentDispositionType;
import org.betaconceptframework.astroboa.api.model.BinaryProperty;
import org.betaconceptframework.astroboa.api.model.CalendarProperty;
import org.betaconceptframework.astroboa.api.model.CmsProperty;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.SimpleCmsProperty;
import org.betaconceptframework.astroboa.api.model.ValueType;
import org.betaconceptframework.astroboa.api.model.io.FetchLevel;
import org.betaconceptframework.astroboa.api.model.io.ImportConfiguration;
import org.betaconceptframework.astroboa.api.model.io.ImportConfiguration.PersistMode;
import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType;
import org.betaconceptframework.astroboa.api.model.query.CacheRegion;
import org.betaconceptframework.astroboa.api.model.query.CmsOutcome;
import org.betaconceptframework.astroboa.api.model.query.Order;
import org.betaconceptframework.astroboa.api.model.query.criteria.ContentObjectCriteria;
import org.betaconceptframework.astroboa.api.security.exception.CmsUnauthorizedAccessException;
import org.betaconceptframework.astroboa.client.AstroboaClient;
import org.betaconceptframework.astroboa.commons.excelbuilder.WorkbookBuilder;
import org.betaconceptframework.astroboa.model.factory.CmsCriteriaFactory;
import org.betaconceptframework.astroboa.model.factory.CriterionFactory;
import org.betaconceptframework.astroboa.resourceapi.utility.ContentApiUtils;
import org.betaconceptframework.astroboa.resourceapi.utility.IndexExtractor;
import org.betaconceptframework.astroboa.util.CmsConstants;
import org.betaconceptframework.astroboa.util.DateUtils;
import org.betaconceptframework.astroboa.util.PropertyExtractor;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartRelatedInput;
import org.jboss.resteasy.util.GenericType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 * 
 */

public class ContentObjectResource extends AstroboaResource {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public ContentObjectResource(AstroboaClient astroboaClient) {
        super(astroboaClient);
    }

    // The methods which produce JSON or XML allow "callback" as one extra query parameter 
    // in order to support XML with Padding or JSON with Padding (JSONP) and overcome the SPO restriction of browsers
    // This means that if a "callback" query parameter is provided then the XML or JSON result will be wrapped inside a "callback" script

    // API URLs for single content object resource

    @GET
    @Produces("*/*")
    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}")
    public Response getContentObjectByIdOrNameAnyResponse(
            @PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
            @QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

        /*if (output == null) {
           return getContentObjectByIdOrName(contentObjectIdOrName, Output.XML, callback,prettyPrintEnabled);
        }*/

        Output outputEnum = ContentApiUtils.getOutputType(output, Output.XML);

        return getContentObjectByIdOrName(contentObjectIdOrName, commaDelimitedProjectionPaths, outputEnum,
                callback, prettyPrintEnabled);
    }

    @GET
    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}")
    public Response getContentObjectByIdAsJson(@PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
            @QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        // URL-based negotiation overrides any Accept header sent by the client
        //i.e. if the url specifies the desired response type in the "output" parameter this method
        // will return the media type specified in "output" request parameter.

        Output outputEnum = Output.JSON;
        if (StringUtils.isNotBlank(output)) {
            outputEnum = Output.valueOf(output.toUpperCase());
        }

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

        return getContentObjectByIdOrName(contentObjectIdOrName, commaDelimitedProjectionPaths, outputEnum,
                callback, prettyPrintEnabled);
    }

    @GET
    @Produces(MediaType.APPLICATION_XML)
    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}")
    public Response getContentObjectByIdAsXml(@PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
            @QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        // URL-based negotiation overrides any Accept header sent by the client
        //i.e. if the url specifies the desired response type in the "output" parameter this method
        // will return the media type specified in "output" request parameter.

        Output outputEnum = Output.XML;
        if (StringUtils.isNotBlank(output)) {
            outputEnum = Output.valueOf(output.toUpperCase());
        }

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

        return getContentObjectByIdOrName(contentObjectIdOrName, commaDelimitedProjectionPaths, outputEnum,
                callback, prettyPrintEnabled);
    }

    // API URLs for content object properties
    //The propertyPath at the end of the URL is the full path to a property value.
    // Property Path segments are separated by dots 
    // For multiple value properties the identifier of the property or the value if it is a binary channel between brackets indicates which one of the values to return.

    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}"
            + "/{propertyPath: " + CmsConstants.PROPERTY_PATH_WITH_ID_REG_EXP_FOR_RESTEASY + "}")
    public AstroboaResource getContentObjectPropertyUsingIdentifierInsteadOfIndex(
            @PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            @PathParam("propertyPath") String propertyPath) {

        try {

            ContentObject contentObject = retrieveContentObjectByIdOrSystemName(contentObjectIdOrName,
                    FetchLevel.ENTITY, null);

            if (contentObject == null) {
                logger.warn(
                        "The provided content object id / system name {} does not correspond to a content object or you do not have permission to access the requested object",
                        contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }

            // We allow to put a mime type suffix (i.e. .jpg, .png, .doc) at the end of the property path
            // This is required when accessing binary properties and the programs which consume the 
            // URL binary outcome do not read the mime type and filename from the header but rather depend on the mime type suffix at 
            // the end of the URL in order to determine how to treat the binary content.
            // Additionally we may utilize this suffix in latter version of the API to support the delivery of different representations 
            // of the property contents. 
            // So we need to check if the property path contains a mime type suffix and remove it
            // This may cause problems if a requested property itself is named under the name of a mime type suffix.
            // To resolve this potential problem it is required to always put a mime type suffix at the end of URLs that read property values 
            // if the requested property is named under the name of a mime type suffix 
            // (i.e. .../objects/{contentObjectId}/myImageWithMultipleFormats.jpg.jpg this will result in removing the last "jpg" suffix but keep the previous one which corresponds to a 
            // property named "jpg") 
            if (propertyPath != null && !propertyPath.endsWith("]")) {
                String candidateMimeTypeSuffix = StringUtils.substringAfterLast(propertyPath, ".");
                if (ContentApiUtils.isKnownMimeTypeSuffix(candidateMimeTypeSuffix)) {
                    propertyPath = StringUtils.substringBeforeLast(propertyPath, ".");
                }
            }

            //Extract property along with the value identifier or the value index
            PropertyExtractor propertyExtractor = null;

            //Load Property according to property path
            CmsProperty property = null;

            try {
                propertyExtractor = new PropertyExtractor(contentObject, propertyPath);
                property = propertyExtractor.getProperty();
            } catch (Exception e) {
                logger.warn("Could not load provided property using path '" + propertyPath + "' from contentObject "
                        + contentObjectIdOrName, e);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }

            if (property == null) {
                logger.warn(
                        "The provided property '{}' for content object with id or system name '{}' does not exist",
                        propertyPath, contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }

            switch (property.getValueType()) {
            case Complex:
                logger.warn(
                        "The provided property '{}' for content object with id or system name '{}' is complex. Currently only simple type property values or binary channel content can be returned through this API call",
                        propertyPath, contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);

            case Binary:
                if (propertyExtractor.getIdentifierOfTheValueOfTheProperty() == null) {
                    return new BinaryChannelResource(astroboaClient, contentObject, (BinaryProperty) property,
                            propertyExtractor.getIndexOfTheValueOfTheProperty());
                } else {
                    return new BinaryChannelResource(astroboaClient, contentObject, (BinaryProperty) property,
                            propertyExtractor.getIdentifierOfTheValueOfTheProperty());
                }

            case ContentType:
                logger.error("Astroboa returned value type '" + ValueType.ContentType
                        + "' for property '{}' for content object with id or system name '{}'. This should never happen",
                        propertyPath, contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);

            default:

                if (propertyExtractor.getIdentifierOfTheValueOfTheProperty() != null) {
                    logger.warn(
                            "The provided property '{}' for content object with id or system name '{}' is a simple non-binary property but user has provided an identifier instead of an index.",
                            propertyPath, contentObjectIdOrName);
                    throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
                }

                return new SimplePropertyResource(astroboaClient, contentObject, (SimpleCmsProperty) property,
                        propertyExtractor.getIndexOfTheValueOfTheProperty());
            }

        } catch (WebApplicationException e) {
            throw e;
        } catch (Exception e) {
            logger.error("A problem occured while retrieving property: '" + propertyPath
                    + "' for content object with id or system name: " + contentObjectIdOrName, e);
            throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
        }

    }

    // API URLs for content object properties
    //The propertyPath at the end of the URL is the full path to a property value.
    // Property Path segments are separated by dots 
    // For multiple value properties an index between brackets indicates which one of the values to return.
    // For example departments.department[0] or departments.department[0].jobPositions.jobPosition[0]
    // The regular expression for the path is ^[A-Za-z0-9_\\-]+(\\[[0-9]+\\])?(\\.[A-Za-z0-9_\\-]+(\\[[0-9]+\\])?)*$

    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}"
            + "/{propertyPath: " + CmsConstants.PROPERTY_PATH_REG_EXP_FOR_RESTEASY + "}")
    public AstroboaResource getContentObjectProperty(
            @PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            @PathParam("propertyPath") String propertyPath) {

        try {

            ContentObject contentObject = retrieveContentObjectByIdOrSystemName(contentObjectIdOrName,
                    FetchLevel.ENTITY, null);

            if (contentObject == null) {
                logger.warn(
                        "The provided content object id / system name {} does not correspond to a content object or you do not have permission to access the requested object",
                        contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }

            // We allow to put a mime type suffix (i.e. .jpg, .png, .doc) at the end of the property path
            // This is required when accessing binary properties and the programs which consume the 
            // URL binary outcome do not read the mime type and filename from the header but rather depend on the mime type suffix at 
            // the end of the URL in order to determine how to treat the binary content.
            // Additionally we may utilize this suffix in latter version of the API to support the delivery of different representations 
            // of the property contents. 
            // So we need to check if the property path contains a mime type suffix and remove it
            // This may cause problems if a requested property itself is named under the name of a mime type suffix.
            // To resolve this potential problem it is required to always put a mime type suffix at the end of URLs that read property values 
            // if the requested property is named under the name of a mime type suffix 
            // (i.e. .../objects/{contentObjectId}/myImageWithMultipleFormats.jpg.jpg this will result in removing the last "jpg" suffix but keep the previous one which corresponds to a 
            // property named "jpg") 
            if (propertyPath != null && !propertyPath.endsWith("]")) {
                String candidateMimeTypeSuffix = StringUtils.substringAfterLast(propertyPath, ".");
                if (ContentApiUtils.isKnownMimeTypeSuffix(candidateMimeTypeSuffix)) {
                    propertyPath = StringUtils.substringBeforeLast(propertyPath, ".");
                }
            }

            //Check if a value index exists and extract it
            IndexExtractor indexExtractor = new IndexExtractor(propertyPath);

            String propertyPathWithoutIndex = indexExtractor.getPropertyPathWithoutIndex();

            int valueIndex = indexExtractor.getIndex();

            //Load Property according to property path
            CmsProperty property = null;

            try {
                property = contentObject.getCmsProperty(propertyPathWithoutIndex);
            } catch (Exception e) {
                logger.warn("Could not load provided property using path '" + propertyPathWithoutIndex
                        + "' from contentObject " + contentObjectIdOrName, e);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }

            if (property == null) {
                logger.warn(
                        "The provided property '{}' for content object with id or system name '{}' does not exist",
                        propertyPathWithoutIndex, contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }

            switch (property.getValueType()) {
            case Complex:
                logger.warn(
                        "The provided property '{}' for content object with id or system name '{}' is complex. Currently only simple type property values or binary channel content can be returned through this API call",
                        propertyPathWithoutIndex, contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);

            case Binary:
                return new BinaryChannelResource(astroboaClient, contentObject, (BinaryProperty) property,
                        valueIndex);

            case ContentType:
                logger.error("Astroboa returned value type '" + ValueType.ContentType
                        + "' for property '{}' for content object with id or system name '{}'. This should never happen",
                        propertyPathWithoutIndex, contentObjectIdOrName);
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);

            default:
                return new SimplePropertyResource(astroboaClient, contentObject, (SimpleCmsProperty) property,
                        valueIndex);
            }

        } catch (WebApplicationException e) {
            throw e;
        } catch (Exception e) {
            logger.error("A problem occured while retrieving property: '" + propertyPath
                    + "' for content object with id or system name: " + contentObjectIdOrName, e);
            throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
        }

    }

    // API URLs for content object resource collections

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public Response getContentObjectsAsXML(@QueryParam("cmsQuery") String cmsQuery,
            @QueryParam("offset") Integer offset, @QueryParam("limit") Integer limit,
            @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
            @QueryParam("orderBy") String orderBy, @QueryParam("output") String output,
            @QueryParam("template") String templateIdOrSystemName, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        // URL-based negotiation overrides any Accept header sent by the client
        //i.e. if the url specifies the desired response type in the "output" parameter this method
        // will return the media type specified in "output" request parameter.
        Output outputEnum = Output.XML;
        if (StringUtils.isNotBlank(output)) {
            outputEnum = Output.valueOf(output.toUpperCase());
        }

        return retrieveContentObjects(cmsQuery, offset, limit, commaDelimitedProjectionPaths, orderBy, outputEnum,
                templateIdOrSystemName, callback, prettyPrint);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getContentObjectsAsJson(@QueryParam("cmsQuery") String cmsQuery,
            @QueryParam("offset") Integer offset, @QueryParam("limit") Integer limit,
            @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
            @QueryParam("orderBy") String orderBy, @QueryParam("output") String output,
            @QueryParam("template") String templateIdOrSystemName, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        // URL-based negotiation overrides any Accept header sent by the client
        //i.e. if the url specifies the desired response type in the "output" parameter this method
        // will return the media type specified in "output" request parameter.
        Output outputEnum = Output.JSON;
        if (StringUtils.isNotBlank(output)) {
            outputEnum = Output.valueOf(output.toUpperCase());
        }

        return retrieveContentObjects(cmsQuery, offset, limit, commaDelimitedProjectionPaths, orderBy, outputEnum,
                templateIdOrSystemName, callback, prettyPrint);
    }

    @GET
    @Produces("application/vnd.ms-excel")
    public Response getContentObjectsInXls(@QueryParam("cmsQuery") String cmsQuery,
            @QueryParam("offset") Integer offset, @QueryParam("limit") Integer limit,
            @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
            @QueryParam("orderBy") String orderBy, @QueryParam("output") String output,
            @QueryParam("template") String templateIdOrSystemName, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        // URL-based negotiation overrides any Accept header sent by the client
        //i.e. if the url specifies the desired response type in the "output" parameter this method
        // will return the media type specified in "output" request parameter.
        Output outputEnum = Output.XLS;
        if (StringUtils.isNotBlank(output)) {
            outputEnum = Output.valueOf(output.toUpperCase());
        }

        return retrieveContentObjects(cmsQuery, offset, limit, commaDelimitedProjectionPaths, orderBy, outputEnum,
                templateIdOrSystemName, callback, prettyPrint);
    }

    /* Returning html or pdf is based on facelets and seam which have been currently
     * removed due to problems with seam resource servlet when multiple seam wars are deployed
    @GET
    @Produces({MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML}) 
    public Response getContentObjectsAsXHTML(
     @QueryParam("cmsQuery") String cmsQuery, 
     @QueryParam("offset") Integer offset, 
     @QueryParam("limit") Integer limit, 
     @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
     @QueryParam("orderBy") String orderBy,
     @QueryParam("output") String output,
     @QueryParam("template") String templateIdOrSystemName,
     @QueryParam("callback") String callback){
           
       // URL-based negotiation overrides any Accept header sent by the client
       //i.e. if the url specifies the desired response type in the "output" parameter this method
       // will return the media type specified in "output" request parameter.
       Output outputEnum = Output.XHTML;
       if (StringUtils.isNotBlank(output)) {
     outputEnum = Output.valueOf(output.toUpperCase());
       }
           
       return retrieveContentObjects(
        cmsQuery, 
        offset, 
        limit, 
        commaDelimitedProjectionPaths, 
        orderBy, 
        outputEnum, 
        templateIdOrSystemName, 
        callback);
           
    }
        
    @GET
    @Produces({"application/pdf"}) 
    public Response getContentObjectsAsPDF(
     @QueryParam("cmsQuery") String cmsQuery, 
     @QueryParam("offset") Integer offset, 
     @QueryParam("limit") Integer limit, 
     @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
     @QueryParam("orderBy") String orderBy,
     @QueryParam("output") String output,
     @QueryParam("template") String templateIdOrSystemName,
     @QueryParam("callback") String callback){
           
       // URL-based negotiation overrides any Accept header sent by the client
       //i.e. if the url specifies the desired response type in the "output" parameter this method
       // will return the media type specified in "output" request parameter.
       Output outputEnum = Output.PDF;
       if (StringUtils.isNotBlank(output)) {
     outputEnum = Output.valueOf(output.toUpperCase());
       }
           
       return retrieveContentObjects(
        cmsQuery, 
        offset, 
        limit, 
        commaDelimitedProjectionPaths, 
        orderBy, 
        outputEnum, 
        templateIdOrSystemName, 
        callback);
           
    }
    */

    @GET
    @Produces("*/*")
    public Response getContentObjects(@QueryParam("cmsQuery") String cmsQuery, @QueryParam("offset") Integer offset,
            @QueryParam("limit") Integer limit, @QueryParam("projectionPaths") String commaDelimitedProjectionPaths,
            @QueryParam("orderBy") String orderBy, @QueryParam("output") String output,
            @QueryParam("template") String templateIdOrSystemName, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        /*if (output == null) {
           return retrieveContentObjects(cmsQuery, offset, limit, commaDelimitedProjectionPaths, orderBy, Output.XML, templateIdOrSystemName, callback);
        }*/

        Output outputEnum = ContentApiUtils.getOutputType(output, Output.XML);

        return retrieveContentObjects(cmsQuery, offset, limit, commaDelimitedProjectionPaths, orderBy, outputEnum,
                templateIdOrSystemName, callback, prettyPrint);

    }

    private Response getContentObjectByIdOrName(String contentObjectIdOrSystemName,
            String commaDelimitedProjectionPaths, Output output, String callback, boolean prettyPrint) {

        if (StringUtils.isBlank(contentObjectIdOrSystemName)) {
            logger.warn("No contentObjectId provided");
            throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
        }

        Date lastModified = null;
        String contentObjectXmlorJson = retrieveContentObjectXMLorJSONByIdOrSystemName(contentObjectIdOrSystemName,
                commaDelimitedProjectionPaths, output, lastModified, prettyPrint);

        if (StringUtils.isBlank(contentObjectXmlorJson)) {
            throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
        }

        try {

            StringBuilder resourceRepresentation = new StringBuilder();

            if (StringUtils.isBlank(callback)) {
                resourceRepresentation.append(contentObjectXmlorJson);
            } else {
                switch (output) {
                case XML: {
                    ContentApiUtils.generateXMLP(resourceRepresentation, contentObjectXmlorJson, callback);
                    break;
                }
                case JSON:
                    ContentApiUtils.generateJSONP(resourceRepresentation, contentObjectXmlorJson, callback);
                    break;
                }

            }

            return ContentApiUtils.createResponse(resourceRepresentation, output, callback, lastModified);

        } catch (Exception e) {
            logger.error("ContentObejct id/name " + contentObjectIdOrSystemName, e);
            throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
        }

    }

    private Response retrieveContentObjects(String cmsQuery, Integer offset, Integer limit,
            String commaDelimitedProjectionPaths, String orderBy, Output output, String templateIdOrSystemName,
            String callback, String prettyPrint) {

        if (output == null) {
            output = Output.XML;
        }

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

        try {
            //Build ContentObject criteria
            ContentObjectCriteria contentObjectCriteria = buildCriteria(cmsQuery, offset, limit,
                    commaDelimitedProjectionPaths, orderBy, prettyPrintEnabled);

            String queryResult = null;

            StringBuilder resourceRepresentation = new StringBuilder();

            switch (output) {
            case XML: {
                queryResult = astroboaClient.getContentService().searchContentObjects(contentObjectCriteria,
                        ResourceRepresentationType.XML);

                if (StringUtils.isBlank(callback)) {
                    resourceRepresentation.append(queryResult);
                } else {
                    ContentApiUtils.generateXMLP(resourceRepresentation, queryResult, callback);
                }
                break;
            }
            case JSON:
                queryResult = astroboaClient.getContentService().searchContentObjects(contentObjectCriteria,
                        ResourceRepresentationType.JSON);
                if (StringUtils.isBlank(callback)) {
                    resourceRepresentation.append(queryResult);
                } else {
                    ContentApiUtils.generateJSONP(resourceRepresentation, queryResult, callback);
                }
                break;
            case XLS:

                CmsOutcome<ContentObject> outcome = astroboaClient.getContentService().searchContentObjects(
                        contentObjectCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);

                WorkbookBuilder workbookBuilder = new WorkbookBuilder(astroboaClient.getDefinitionService(), "en");

                int rowIndex = 2;
                for (ContentObject object : outcome.getResults()) {

                    workbookBuilder.addContentObjectToWorkbook(object);

                    ++rowIndex;

                    //Limit to the first 5000 content objects
                    if (rowIndex > 5000) {
                        break;
                    }
                }

                String filename = createFilename(workbookBuilder);

                return ContentApiUtils.createResponseForExcelWorkbook(workbookBuilder.getWorkbook(),
                        ContentDispositionType.ATTACHMENT, filename + ".xls", null);
            /* This functionality is temporarily removed until the resolution of seam resource servlet problems 
             * when multiple wars are deployed   
            case XHTML:
            {
               List<ContentObject> contentObjects = searchContentObjects(contentObjectCriteria);
               Contexts.getEventContext().set("contentObjects", contentObjects);
               Contexts.getEventContext().set("repositoryId", AstroboaClientContextHolder.getActiveRepositoryId());
               Contexts.getEventContext().set("templateObjectIdOrSystemName", templateIdOrSystemName);
               Contexts.getEventContext().set("templateProperty", "xhtml");
               Renderer renderer = Renderer.instance();
               String xhtmlOutput = renderer.render("/dynamicPage.xhtml");
               if (StringUtils.isNotBlank(xhtmlOutput)) {
            resourceRepresentation.append(xhtmlOutput);
               }
               break;
            }
            case PDF:
            {
               List<ContentObject> contentObjects = searchContentObjects(contentObjectCriteria);
               Contexts.getEventContext().set("contentObjects", contentObjects);
               Contexts.getEventContext().set("repositoryId", AstroboaClientContextHolder.getActiveRepositoryId());
               Contexts.getEventContext().set("templateObjectIdOrSystemName", templateIdOrSystemName);
               Contexts.getEventContext().set("templateProperty", "pdf");
                
               byte[] pdfBytes = createPDF("");
                   
               return ContentApiUtils.createBinaryResponse(
               pdfBytes, 
               "application/pdf", 
               ContentDispositionType.ATTACHMENT, 
               contentObjectCriteria.getXPathQuery() + ".pdf", null);
            }
            */
            }

            return ContentApiUtils.createResponse(resourceRepresentation, output, callback, null);

        } catch (Exception e) {
            return ContentApiUtils.createResponseForException(Response.Status.BAD_REQUEST, e, true,
                    "Cms Query " + cmsQuery);
        }
    }

    private ContentObjectCriteria buildCriteria(String cmsQuery, Integer offset, Integer limit,
            String commaDelimitedProjectionPaths, String orderBy, boolean prettyPrint) {

        //Build ContentObject criteria
        ContentObjectCriteria contentObjectCriteria = CmsCriteriaFactory.newContentObjectCriteria();

        if (offset == null || offset < 0) {
            contentObjectCriteria.setOffset(0);
        } else {
            contentObjectCriteria.setOffset(offset);
        }

        if (limit == null || limit < 0) {
            contentObjectCriteria.setLimit(50);
        } else {
            contentObjectCriteria.setLimit(limit);
        }

        if (StringUtils.isNotBlank(commaDelimitedProjectionPaths)) {
            contentObjectCriteria.getPropertyPathsWhichWillBePreLoaded()
                    .addAll(Arrays.asList(commaDelimitedProjectionPaths.split(CmsConstants.COMMA)));
        } else {
            contentObjectCriteria.getRenderProperties().renderAllContentObjectProperties(true);
        }

        contentObjectCriteria.getRenderProperties().prettyPrint(prettyPrint);

        //Parse query
        if (StringUtils.isNotBlank(cmsQuery)) {
            CriterionFactory.parse(cmsQuery, contentObjectCriteria);
        } else {
            logger.warn(
                    "No query parameter was found. All content objects will be returned according to limit {} and offset {}",
                    contentObjectCriteria.getLimit(), contentObjectCriteria.getOffset());
        }

        //Parse order by
        //Order by value expects to follow pattern
        // property.path asc,property.path2 desc,property.path3
        if (StringUtils.isNotBlank(orderBy)) {
            String[] propertyPathsWithOrder = StringUtils.split(orderBy, ",");

            if (!ArrayUtils.isEmpty(propertyPathsWithOrder)) {
                for (String propertyWithOrder : propertyPathsWithOrder) {
                    String[] propertyItems = StringUtils.split(propertyWithOrder, " ");

                    if (!ArrayUtils.isEmpty(propertyItems) && propertyItems.length == 2) {
                        String property = propertyItems[0];
                        String order = propertyItems[1];

                        //Check to see if order is valid
                        if (StringUtils.isNotBlank(property)) {
                            if (StringUtils.equals("desc", order)) {
                                contentObjectCriteria.addOrderProperty(property, Order.descending);
                            } else {
                                //Any other value (even invalid) set default order which is ascending
                                contentObjectCriteria.addOrderProperty(property, Order.ascending);
                            }
                        }
                    }
                }
            }

        }

        return contentObjectCriteria;

    }

    private ContentObject retrieveContentObjectByIdOrSystemName(String contentObjectIdOrSystemName,
            FetchLevel fetchLevel, List<String> propertyPaths) {
        try {

            return astroboaClient.getContentService().getContentObject(contentObjectIdOrSystemName,
                    ResourceRepresentationType.CONTENT_OBJECT_INSTANCE, fetchLevel, CacheRegion.NONE, propertyPaths,
                    false);
        } catch (Exception e) {
            return null;
        }
    }

    private String retrieveContentObjectXMLorJSONByIdOrSystemName(String contentObjectIdOrSystemName,
            String commaDelimitedProjectionPaths, Output output, Date lastModified, boolean prettyPrint) {
        try {
            ContentObject contentObject = null;
            String[] propertyPathsArray = null;

            if (StringUtils.isBlank(commaDelimitedProjectionPaths)) {
                contentObject = retrieveContentObjectByIdOrSystemName(contentObjectIdOrSystemName, FetchLevel.FULL,
                        null);
            } else {
                propertyPathsArray = commaDelimitedProjectionPaths.split(",");
                List<String> propertyPaths = Arrays.asList(propertyPathsArray);
                contentObject = retrieveContentObjectByIdOrSystemName(contentObjectIdOrSystemName,
                        FetchLevel.ENTITY, propertyPaths);
            }

            if (contentObject == null) {
                return null;
            }

            lastModified = ((CalendarProperty) contentObject.getCmsProperty("profile.modified"))
                    .getSimpleTypeValueAsDate();

            //Default output is XML
            if (output == null) {
                return contentObject.xml(prettyPrint);
            }

            switch (output) {
            case XML:
                return contentObject.xml(prettyPrint, false, propertyPathsArray);
            case JSON:
                return contentObject.json(prettyPrint, false, propertyPathsArray);
            default:
                return contentObject.xml(prettyPrint, false, propertyPathsArray);
            }
        } catch (Exception e) {
            return null;
        }
    }

    /* This functionality is temporarily removed until the resolution of seam resource servlet problems 
     * when multiple wars are deployed
    private byte[] createPDF(String path) {
       // String DATA_STORE =
       // "org.jboss.seam.document.documentStore.dataStore";
       //EmptyFacesContext emptyFacesContext = new EmptyFacesContext();
       byte[] bytes = null;
        
       try {
     Renderer render = Renderer.instance(); 
         
     render.render("/dynamicPage.xhtml");
     DocumentStore doc = DocumentStore.instance();
        
     if (doc != null) {
        DocumentData data = doc.getDocumentData("1");
        ByteArrayDocumentData byteData = null;
        if (data instanceof ByteArrayDocumentData) {
           byteData = (ByteArrayDocumentData) data;
        } else {
           throw new IllegalArgumentException("Couldnt get the bytes from the pdf document, unkown class " + data.getClass().getName());
        }
        bytes = byteData.getData();
     }
        
       } catch (Exception ex) {
     logger.error("Error when trying to get the content of the pdf in bytes with the message #0", ex.getMessage());
     ex.printStackTrace();
       } finally {
     //emptyFacesContext.restore();
       }
        
       return bytes;
    }
    */

    private List<ContentObject> searchContentObjects(ContentObjectCriteria contentObjectCriteria) {
        CmsOutcome<ContentObject> cmsOutcome = astroboaClient.getContentService()
                .searchContentObjects(contentObjectCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);

        if (cmsOutcome.getResults() != null) {
            return cmsOutcome.getResults();
        }

        return new ArrayList<ContentObject>();
    }

    @POST
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response postContentObject(String requestContent) {

        long start = System.currentTimeMillis();

        Response response = saveContentObjectString(requestContent, HttpMethod.POST, true, true);

        logger.debug(" POST ContentObject in {}",
                DurationFormatUtils.formatDurationHMS(System.currentTimeMillis() - start));

        return response;

    }

    @PUT
    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response putContentObjectByIdOrName(@PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            @QueryParam("updateLastModificationTime") String updateLastModificationTime, String requestContent) {

        if (StringUtils.isBlank(contentObjectIdOrName)) {
            logger.warn("Use HTTP PUT to save object {} but no id or system name was provided ", requestContent);
            throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
        }

        long start = System.currentTimeMillis();

        Response response = saveContentObjectByIdOrName(contentObjectIdOrName, requestContent, HttpMethod.PUT,
                ContentApiUtils.shouldUpdateLastModificationTime(updateLastModificationTime));

        logger.debug(" PUT ContentObject {} in {}", contentObjectIdOrName,
                DurationFormatUtils.formatDurationHMS(System.currentTimeMillis() - start));

        return response;

    }

    @DELETE
    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}")
    public Response deleteContentObjectByIdOrName(
            @PathParam("contentObjectIdOrName") String contentObjectIdOrName) {

        if (StringUtils.isBlank(contentObjectIdOrName)) {
            logger.warn("No id or system name was provided. Delete request cannot proceed");
            throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
        }

        try {
            boolean objectDeleted = astroboaClient.getContentService().deleteContentObject(contentObjectIdOrName);

            return ContentApiUtils.createResponseForHTTPDelete(objectDeleted, contentObjectIdOrName);
        } catch (CmsUnauthorizedAccessException e) {
            throw new WebApplicationException(HttpURLConnection.HTTP_UNAUTHORIZED);
        } catch (Exception e) {
            logger.error("", e);
            throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
        }
    }

    @POST
    @Consumes("multipart/related")
    public Response postObjectMultipartRelated(MultipartRelatedInput multipartRelatedInput) {
        return saveContentFromMultipartRelatedRequest(null, multipartRelatedInput, HttpMethod.POST, true);
    }

    @PUT
    @Consumes("multipart/related")
    @Path("/{contentObjectIdOrName: " + CmsConstants.UUID_OR_SYSTEM_NAME_REG_EXP_FOR_RESTEASY + "}")
    public Response putObjectMultipartRelated(@PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            @QueryParam("updateLastModificationTime") String updateLastModificationTime,
            MultipartRelatedInput multipartRelatedInput) {

        return saveContentFromMultipartRelatedRequest(contentObjectIdOrName, multipartRelatedInput, HttpMethod.PUT,
                ContentApiUtils.shouldUpdateLastModificationTime(updateLastModificationTime));
    }

    private Response saveContentFromMultipartRelatedRequest(String contentObjectIdOrName,
            MultipartRelatedInput multipartRelatedInput, String httpMethod, boolean updateLastModificationTime) {

        try {

            //Obtain the part which contains the object's JSON/XML
            InputPart partWhichContainsObjectSource = getMessagePartWithObjectSource(multipartRelatedInput);

            //Check that mime type of the content is a valid one
            ResourceRepresentationType resourceRepresentationType = checkMediaTypeIsValid(
                    partWhichContainsObjectSource);

            ContentObject contentObjectToBeSaved = retrieveObjectSourceFromMessageAndImportWithoutSave(
                    multipartRelatedInput, partWhichContainsObjectSource);

            boolean entityIsNew = objectIsNew(contentObjectIdOrName, httpMethod, contentObjectToBeSaved);

            //Save content object
            try {
                contentObjectToBeSaved = astroboaClient.getContentService().save(contentObjectToBeSaved, false,
                        updateLastModificationTime, null);

                return ContentApiUtils.createSuccessfulResponseForPUTOrPOST(contentObjectToBeSaved, httpMethod,
                        resourceRepresentationType, entityIsNew);

            } catch (CmsUnauthorizedAccessException e) {
                throw new WebApplicationException(HttpURLConnection.HTTP_UNAUTHORIZED);
            } catch (Exception e) {
                logger.error("", e);
                throw new WebApplicationException(e, HttpURLConnection.HTTP_INTERNAL_ERROR);
            }

        } catch (WebApplicationException e) {
            throw e;
        } catch (Exception e) {
            throw new WebApplicationException(
                    ContentApiUtils.createResponseForException(Status.INTERNAL_SERVER_ERROR, e, true,
                            "A problem occured while saving form data for object with id or system name: "
                                    + contentObjectIdOrName));
        }
    }

    private ContentObject retrieveObjectSourceFromMessageAndImportWithoutSave(
            MultipartRelatedInput multipartRelatedInput, InputPart partWhichContainsObjectSource)
            throws IOException {

        Map<String, byte[]> binaryContentMap = new HashMap<String, byte[]>();

        //Iterate through the input parts to collect binary data
        for (Entry<String, InputPart> inputPartEntry : multipartRelatedInput.getRelatedMap().entrySet()) {

            String partId = inputPartEntry.getKey();

            if (StringUtils.equals(partId, multipartRelatedInput.getStart())) {
                //Do not process root part
                continue;
            }

            InputPart inputPart = inputPartEntry.getValue();

            boolean base64Encoded = partIsBase64Encoded(inputPart.getHeaders());

            byte[] binaryContent = inputPart.getBody(new GenericType<byte[]>() {
            });

            if (base64Encoded) {
                binaryContent = Base64.decodeBase64(binaryContent);
            }

            binaryContentMap.put(partId, binaryContent);
        }

        //Import content but do not save it 
        ImportConfiguration importConfiguration = ImportConfiguration.object().persist(PersistMode.DO_NOT_PERSIST)
                .addBinaryContent(binaryContentMap).build();

        ContentObject contentObjectToBeSaved = astroboaClient.getImportService()
                .importContentObject(partWhichContainsObjectSource.getBodyAsString(), importConfiguration);

        if (logger.isDebugEnabled()) {
            logger.debug("XML output of imported content object \n{}", contentObjectToBeSaved.xml(true));
        }

        return contentObjectToBeSaved;
    }

    private boolean partIsBase64Encoded(MultivaluedMap<String, String> headers) {

        if (headers != null && headers.isEmpty()) {
            for (Entry<String, List<String>> headerEntry : headers.entrySet()) {
                if (StringUtils.equalsIgnoreCase(headerEntry.getKey(), "Content-Transfer-Encoding")) {

                    if (headerEntry.getValue() != null && headerEntry.getValue().contains("base64")) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    private ResourceRepresentationType checkMediaTypeIsValid(InputPart partWhichContainsObjectSource) {

        MediaType mediaType = partWhichContainsObjectSource.getMediaType();

        if (mediaType == null) {
            throw new WebApplicationException(ContentApiUtils.createResponseForException(Status.BAD_REQUEST, null,
                    false, "No Content-Type Header found for object content"));
        }

        if (mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE)) {
            return ResourceRepresentationType.JSON;
        } else if (mediaType.isCompatible(MediaType.APPLICATION_XML_TYPE)) {
            return ResourceRepresentationType.XML;
        } else if (mediaType.isCompatible(MediaType.TEXT_PLAIN_TYPE)) {
            return null;
        } else {
            throw new WebApplicationException(ContentApiUtils.createResponseForException(Status.BAD_REQUEST, null,
                    false, "Invalid Content-Type Header " + mediaType.toString() + " found for object content"));
        }
    }

    //According to the RestEASY API, method getRootPart()
    //returns the root part of the message. 
    //If a start parameter was set in the message header 
    //the part with that id is returned. 
    //If no start parameter was set the first part is returned.
    private InputPart getMessagePartWithObjectSource(MultipartRelatedInput multipartRelatedInput) {

        InputPart partWhichContainsObjectSource = multipartRelatedInput.getRootPart();

        if (partWhichContainsObjectSource == null) {
            if (StringUtils.isBlank(multipartRelatedInput.getStart())) {
                throw new WebApplicationException(ContentApiUtils.createResponseForException(Status.BAD_REQUEST,
                        null, false,
                        "'start' parameter in Content-Type header is not provided. There is no way ot determine which part of the message contains the object's content"));
            } else {
                throw new WebApplicationException(ContentApiUtils.createResponseForException(Status.BAD_REQUEST,
                        null, false, "Could not locate message part with id " + multipartRelatedInput.getStart()
                                + ". Object's content is not provided"));
            }
        }

        return partWhichContainsObjectSource;
    }

    private Response saveContentObjectByIdOrName(@PathParam("contentObjectIdOrName") String contentObjectIdOrName,
            String requestContent, String httpMethod, boolean updateLastModificationTime) {

        //Import from xml or json. ContentObject will not be saved
        ImportConfiguration importConfiguration = ImportConfiguration.object().persist(PersistMode.DO_NOT_PERSIST)
                .updateLastModificationTime(updateLastModificationTime).build();

        ContentObject contentObjectToBeSaved = astroboaClient.getImportService().importContentObject(requestContent,
                importConfiguration);

        if (logger.isDebugEnabled()) {
            logger.debug("XML output of imported content object \n{}", contentObjectToBeSaved.xml(true));
        }

        boolean entityIsNew = objectIsNew(contentObjectIdOrName, httpMethod, contentObjectToBeSaved);

        //Save content object
        return saveContentObject(contentObjectToBeSaved, httpMethod, requestContent, entityIsNew,
                updateLastModificationTime);
    }

    private boolean objectIsNew(String contentObjectIdOrName, String httpMethod,
            ContentObject contentObjectToBeSaved) {

        if (contentObjectIdOrName == null) {
            return true;
        }

        ContentObject existingObject = astroboaClient.getContentService().getContentObject(contentObjectIdOrName,
                ResourceRepresentationType.CONTENT_OBJECT_INSTANCE, FetchLevel.ENTITY, CacheRegion.NONE, null,
                false);

        boolean entityIsNew = existingObject == null;

        if (CmsConstants.UUIDPattern.matcher(contentObjectIdOrName).matches()) {
            //Save content object by Id

            if (contentObjectToBeSaved.getId() == null) {
                contentObjectToBeSaved.setId(contentObjectIdOrName);
            } else {
                //Payload contains id. Check if they are the same
                if (!StringUtils.equals(contentObjectIdOrName, contentObjectToBeSaved.getId())) {
                    logger.warn("Try to " + httpMethod + " content object with ID " + contentObjectIdOrName
                            + " but payload contains id " + contentObjectToBeSaved.getId());
                    throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
                }
            }
        } else {
            //Save content object by SystemName
            //Check that payload contains id
            if (contentObjectToBeSaved.getId() == null) {
                if (existingObject != null) {
                    //A content object with system name 'contentObjectIdOrName' exists, but in payload no id was provided
                    //Set this id to ContentObject representing the payload
                    contentObjectToBeSaved.setId(existingObject.getId());
                }
            } else {

                //Payload contains an id. 

                if (existingObject != null) {
                    //if this is not the same with the id returned from repository raise an exception
                    if (!StringUtils.equals(existingObject.getId(), contentObjectToBeSaved.getId())) {
                        logger.warn(
                                "Try to " + httpMethod + " content object with system name " + contentObjectIdOrName
                                        + " which corresponds to an existed content object in repository with id "
                                        + existingObject.getId() + " but payload contains a different id "
                                        + contentObjectToBeSaved.getId());
                        throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
                    }
                }
            }
        }
        return entityIsNew;
    }

    private Response saveContentObjectString(String contentSource, String httpMethod, boolean entityIsNew,
            boolean updateLastModificationTime) {

        try {

            //Must determine whether a single or a collection of objects is saved
            if (contentSource == null) {
                throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
            }

            if (contentSource.contains(CmsConstants.RESOURCE_RESPONSE_PREFIXED_NAME)
                    || contentSource.contains(CmsConstants.RESOURCE_COLLECTION)) {

                List<ContentObject> contentObjects = astroboaClient.getContentService()
                        .saveContentObjectResourceCollection(contentSource, false, updateLastModificationTime,
                                null);

                //TODO : Improve response details.

                ResponseBuilder responseBuilder = Response.status(Status.OK);

                responseBuilder.header("Content-Disposition", "inline");
                responseBuilder.type(MediaType.TEXT_PLAIN + "; charset=utf-8");

                return responseBuilder.build();

            } else {

                ContentObject contentObject = astroboaClient.getContentService().save(contentSource, false,
                        updateLastModificationTime, null);

                return ContentApiUtils.createResponseForPutOrPostOfACmsEntity(contentObject, httpMethod,
                        contentSource, entityIsNew);
            }

        } catch (CmsUnauthorizedAccessException e) {
            throw new WebApplicationException(HttpURLConnection.HTTP_UNAUTHORIZED);
        } catch (Exception e) {
            logger.error("", e);
            throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
        }
    }

    private Response saveContentObject(ContentObject contentObject, String httpMethod, String requestContent,
            boolean entityIsNew, boolean updateLastModificationTime) {

        try {
            contentObject = astroboaClient.getContentService().save(contentObject, false,
                    updateLastModificationTime, null);

            return ContentApiUtils.createResponseForPutOrPostOfACmsEntity(contentObject, httpMethod, requestContent,
                    entityIsNew);

        } catch (CmsUnauthorizedAccessException e) {
            throw new WebApplicationException(HttpURLConnection.HTTP_UNAUTHORIZED);
        } catch (Exception e) {
            logger.error("", e);
            throw new WebApplicationException(e, HttpURLConnection.HTTP_INTERNAL_ERROR);
        }
    }

    private String createFilename(WorkbookBuilder workbookBuilder) {
        String filename = workbookBuilder.getWorkbookName();

        if (filename.length() > 50) {
            filename = filename.substring(0, 49);
        }

        filename = filename + "-" + DateUtils.format(Calendar.getInstance(), "ddMMyyyyHHmm");
        return filename;
    }

}