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

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.resourceapi.resource.DefinitionResource.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.File;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Map;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
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.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.definition.CmsDefinition;
import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType;
import org.betaconceptframework.astroboa.api.service.DefinitionService;
import org.betaconceptframework.astroboa.client.AstroboaClient;
import org.betaconceptframework.astroboa.resourceapi.utility.ContentApiUtils;
import org.betaconceptframework.astroboa.resourceapi.utility.XmlSchemaGenerator;
import org.betaconceptframework.astroboa.serializer.ModelSerializer;
import org.betaconceptframework.astroboa.util.CmsConstants;
import org.betaconceptframework.astroboa.util.ResourceApiURLUtils;
import org.betaconceptframework.astroboa.util.UrlProperties;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 * 
 */
public class DefinitionResource extends AstroboaResource {

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

    public DefinitionResource(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
    @GET
    @Produces("*/*")
    @Path("/{propertyPath: " + CmsConstants.PROPERTY_PATH_REG_EXP_FOR_RESTEASY + "}")
    public Response getDefinition(@PathParam("propertyPath") String propertyPath,
            @QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

        if (output == null) {

            if (propertyPath != null && propertyPath.endsWith(".xsd")) {
                //user has provided a file name and not a property path
                return getDefinitionInternal(propertyPath, Output.XSD, callback, prettyPrintEnabled);
            }

            return getDefinitionInternal(propertyPath, Output.XML, callback, prettyPrintEnabled);
        }

        Output outputEnum = Output.valueOf(output.toUpperCase());

        if (outputEnum != Output.XSD && asrtoboaBuiltInModelIsRequested(propertyPath)) {
            //User has requested astroboa-model or astroboa-api built in schemata
            //but at the same time, the "output" request parameter was not XSD
            //In this case an HTTP NOT FOUND error should be returned.
            throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
        }

        return getDefinitionInternal(propertyPath, outputEnum, callback, prettyPrintEnabled);

    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/{propertyPath: " + CmsConstants.PROPERTY_PATH_REG_EXP_FOR_RESTEASY + "}")
    public Response getDefinitionAsJson(@PathParam("propertyPath") String propertyPath,
            @QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(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());

            if (outputEnum != Output.XSD && asrtoboaBuiltInModelIsRequested(propertyPath)) {
                //User has requested astroboa-model or astroboa-api built in schemata
                //but at the same time, the "output" request parameter was not XSD
                //In this case an HTTP NOT FOUND error should be returned.
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }
        }

        return getDefinitionInternal(propertyPath, outputEnum, callback, prettyPrintEnabled);
    }

    private boolean asrtoboaBuiltInModelIsRequested(String propertyPath) {
        return StringUtils.equals(propertyPath, CmsConstants.ASTROBOA_MODEL_SCHEMA_FILENAME_WITH_VERSION)
                || StringUtils.equals(propertyPath, CmsConstants.ASTROBOA_API_SCHEMA_FILENAME_WITH_VERSION);
    }

    @GET
    @Produces(MediaType.APPLICATION_XML)
    @Path("/{propertyPath: " + CmsConstants.PROPERTY_PATH_REG_EXP_FOR_RESTEASY + "}")
    public Response getDefinitionAsXml(@PathParam("propertyPath") String propertyPath,
            @QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint, @Context UriInfo uriInfo) {

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(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());

            if (outputEnum != Output.XSD && asrtoboaBuiltInModelIsRequested(propertyPath)) {
                //User has requested astroboa-model or astroboa-api built in schemata
                //but at the same time, the "output" request parameter was not XSD
                //In this case an HTTP NOT FOUND error should be returned.
                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
            }
        } else {
            //User did not provide value for the "output" parameter therefore 
            //the output at this point is XML. However there is one case where the
            //user may have provide the suffix ".xsd" in the URL, requesting this
            //way the output to be an XML Schema. This case can only be traced by 
            //examining the path of the request and whether it ends in ".xsd" or not
            String path = uriInfo.getPath();

            if (StringUtils.endsWith(path, ".xsd")) {
                //User has provided the suffix ".xsd". 
                //Therefore an XML Schema sholud be returned
                //We have to attach the suffix to the propertyPath variable
                //since the user may have specified a filename instead of 
                //a property path.
                outputEnum = Output.XSD;
                if (propertyPath != null) {
                    propertyPath += ".xsd";
                }
            }
        }

        return getDefinitionInternal(propertyPath, outputEnum, callback, prettyPrintEnabled);
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response postDefinition(String source) {

        try {

            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> objectType = mapper.readValue(source, Map.class);

            XmlSchemaGenerator xmlSchemaTemplate = new XmlSchemaGenerator();

            String schema = xmlSchemaTemplate.generateXmlSchema(objectType);

            //Save schema to schemata folder
            String objectTypeName = (String) objectType.get("name");

            File schemaFile = new File(
                    astroboaClient.getRepositoryService().getCurrentConnectedRepository()
                            .getRepositoryHomeDirectory() + File.separator + "astroboa_schemata",
                    objectTypeName + ".xsd");

            FileUtils.writeStringToFile(schemaFile, schema);

            //Call definition service to force Astroboa to load newly created definition
            CmsDefinition newlyCreatedDefinition = astroboaClient.getDefinitionService()
                    .getCmsDefinition(objectTypeName, ResourceRepresentationType.DEFINITION_INSTANCE, false);

            if (newlyCreatedDefinition == null) {
                throw new Exception("Object type " + objectTypeName + " was not created.");
            }

            //Generate Response
            ResponseBuilder responseBuilder = Response.status(Status.CREATED);

            UrlProperties urlProperties = new UrlProperties();
            urlProperties.setResourceRepresentationType(null);
            urlProperties.setFriendly(false);
            urlProperties.setRelative(false);
            urlProperties.setIdentifier(newlyCreatedDefinition.getName());

            responseBuilder.location(
                    URI.create(ResourceApiURLUtils.generateUrlForEntity(newlyCreatedDefinition, urlProperties)));

            return Response.ok().build();

        } catch (Exception e) {
            e.printStackTrace();
            return ContentApiUtils.createResponseForException(Status.BAD_REQUEST, e, true,
                    "Definition source " + source);
        }

    }

    private Response getDefinitionInternal(String propertyPath, Output output, String callback,
            boolean prettyPrint) {

        try {

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

            DefinitionService definitionService = astroboaClient.getDefinitionService();

            StringBuilder definitionAsXMLOrJSONorXSD = new StringBuilder();

            switch (output) {
            case XML: {

                String xml = definitionService.getCmsDefinition(propertyPath, ResourceRepresentationType.XML,
                        prettyPrint);

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

                if (StringUtils.isBlank(callback)) {
                    definitionAsXMLOrJSONorXSD.append(xml);
                } else {
                    ContentApiUtils.generateXMLP(definitionAsXMLOrJSONorXSD, xml, callback);
                }
                break;
            }
            case JSON:

                String json = definitionService.getCmsDefinition(propertyPath, ResourceRepresentationType.JSON,
                        prettyPrint);

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

                if (StringUtils.isBlank(callback)) {
                    definitionAsXMLOrJSONorXSD.append(json);
                } else {
                    ContentApiUtils.generateJSONP(definitionAsXMLOrJSONorXSD, json, callback);
                }
                break;
            case XSD:

                String xsd = definitionService.getCmsDefinition(propertyPath, ResourceRepresentationType.XSD,
                        prettyPrint);

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

                definitionAsXMLOrJSONorXSD.append(xsd);

                if (StringUtils.isNotBlank(callback)) {
                    logger.warn("Callback {} is ignored in {} output", callback, output);
                }
                break;
            }

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

        } catch (WebApplicationException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Definition For property path: " + propertyPath, e);
            throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
        }
    }

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public Response getModelAsXml(@QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

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

        return getModelInternal(callback, prettyPrintEnabled, outputEnum);

    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getModelAsJson(@QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

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

        return getModelInternal(callback, prettyPrintEnabled, outputEnum);

    }

    @GET
    @Produces("*/*")
    public Response getModel(@QueryParam("output") String output, @QueryParam("callback") String callback,
            @QueryParam("prettyPrint") String prettyPrint) {

        boolean prettyPrintEnabled = ContentApiUtils.isPrettyPrintEnabled(prettyPrint);

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

        return getModelInternal(callback, prettyPrintEnabled, outputEnum);

    }

    private Response getModelInternal(String callback, boolean prettyPrintEnabled, Output outputEnum) {
        try {

            if (outputEnum != Output.XML && outputEnum != Output.JSON) {
                throw new Exception("All definitions are exported only in XML and JSON format. " + outputEnum
                        + " is not supported");
            }

            String allDefinitions = new ModelSerializer(prettyPrintEnabled, outputEnum == Output.JSON,
                    astroboaClient.getDefinitionService(), astroboaClient.getConnectedRepositoryId()).serialize();

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

            StringBuilder definitionAsXMLOrJSONorXSD = new StringBuilder();
            if (StringUtils.isBlank(callback)) {
                definitionAsXMLOrJSONorXSD.append(allDefinitions);
            } else {
                switch (outputEnum) {
                case XML: {
                    ContentApiUtils.generateXMLP(definitionAsXMLOrJSONorXSD, allDefinitions, callback);
                    break;
                }
                case JSON:
                    ContentApiUtils.generateJSONP(definitionAsXMLOrJSONorXSD, allDefinitions, callback);
                    break;
                }
            }

            return ContentApiUtils.createResponse(definitionAsXMLOrJSONorXSD, outputEnum, callback, null);

        } catch (Exception e) {
            logger.error("When exporting all Definitions", e);
            throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
        }
    }
}