com.temenos.interaction.media.odata.xml.atom.AtomXMLProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.temenos.interaction.media.odata.xml.atom.AtomXMLProvider.java

Source

package com.temenos.interaction.media.odata.xml.atom;

/*
 * #%L
 * interaction-media-odata-xml
 * %%
 * Copyright (C) 2012 - 2013 Temenos Holdings N.V.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import org.apache.commons.io.IOUtils;
import org.odata4j.core.OEntities;
import org.odata4j.core.OEntity;
import org.odata4j.core.OEntityKey;
import org.odata4j.core.OLink;
import org.odata4j.core.OLinks;
import org.odata4j.edm.EdmDataServices;
import org.odata4j.edm.EdmEntitySet;
import org.odata4j.edm.EdmEntityType;
import org.odata4j.exceptions.NotFoundException;
import org.odata4j.format.Entry;
import org.odata4j.format.xml.AtomEntryFormatParserExt;
import org.odata4j.internal.InternalUtil;
import org.odata4j.producer.Responses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.temenos.interaction.core.ExtendedMediaTypes;
import com.temenos.interaction.core.UriInfoImpl;
import com.temenos.interaction.core.command.InteractionContext;
import com.temenos.interaction.core.entity.Entity;
import com.temenos.interaction.core.entity.EntityProperties;
import com.temenos.interaction.core.entity.EntityProperty;
import com.temenos.interaction.core.entity.Metadata;
import com.temenos.interaction.core.hypermedia.BeanTransformer;
import com.temenos.interaction.core.hypermedia.CollectionResourceState;
import com.temenos.interaction.core.hypermedia.DefaultResourceStateProvider;
import com.temenos.interaction.core.hypermedia.HypermediaTemplateHelper;
import com.temenos.interaction.core.hypermedia.Link;
import com.temenos.interaction.core.hypermedia.MethodNotAllowedException;
import com.temenos.interaction.core.hypermedia.ResourceState;
import com.temenos.interaction.core.hypermedia.ResourceStateMachine;
import com.temenos.interaction.core.hypermedia.ResourceStateProvider;
import com.temenos.interaction.core.hypermedia.Transformer;
import com.temenos.interaction.core.hypermedia.Transition;
import com.temenos.interaction.core.resource.CollectionResource;
import com.temenos.interaction.core.resource.EntityResource;
import com.temenos.interaction.core.resource.RESTResource;
import com.temenos.interaction.core.resource.ResourceTypeHelper;
import com.temenos.interaction.core.rim.URLHelper;
import com.temenos.interaction.core.web.RequestContext;
import com.temenos.interaction.odataext.entity.MetadataOData4j;

@Provider
@Consumes({ MediaType.APPLICATION_ATOM_XML })
@Produces({ ExtendedMediaTypes.APPLICATION_ATOMSVC_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML })
public class AtomXMLProvider implements MessageBodyReader<RESTResource>, MessageBodyWriter<RESTResource> {
    private static final String UTF_8 = "UTF-8";
    private static final Logger LOGGER = LoggerFactory.getLogger(AtomXMLProvider.class);
    private static final Pattern STRING_KEY_RESOURCE_PATTERN = Pattern.compile("(\\('.*'\\))");

    @Context
    private UriInfo uriInfo;
    @Context
    private Request requestContext;
    private AtomEntryFormatWriter entryWriter;
    private AtomFeedFormatWriter feedWriter;
    private AtomEntityEntryFormatWriter entityEntryWriter;

    private final MetadataOData4j metadataOData4j;
    private final Metadata metadata;
    private final ResourceState serviceDocument;
    private final Transformer transformer;
    private final LinkInterceptor linkInterceptor = new ODataLinkInterceptor(this);

    private ResourceStateProvider resourceStateProvider;

    /**
     * Construct the jax-rs Provider for OData media type.
     * @param metadataOData4j
     *       The entity metadata for reading and writing OData entities.
     * @param metadata
     *       The entity metadata for reading and writing Entity entities.
     * @param hypermediaEngine
     *       The hypermedia engine contains all the resource to entity mappings
     * @param transformer
     *       Transformer to convert an entity to a properties map
     */
    public AtomXMLProvider(MetadataOData4j metadataOData4j, Metadata metadata,
            ResourceStateMachine hypermediaEngine, Transformer transformer) {
        this(metadataOData4j, metadata, new DefaultResourceStateProvider(hypermediaEngine),
                hypermediaEngine.getResourceStateByName("ServiceDocument"), transformer);
    }

    public AtomXMLProvider(MetadataOData4j metadataOData4j, Metadata metadata,
            ResourceStateProvider resourceStateProvider, ResourceState serviceDocument, Transformer transformer) {
        this.metadataOData4j = metadataOData4j;
        this.metadata = metadata;
        this.resourceStateProvider = resourceStateProvider;
        assert resourceStateProvider != null;
        this.serviceDocument = serviceDocument;
        if (serviceDocument == null)
            throw new RuntimeException("No 'ServiceDocument' found.");
        assert metadata != null;
        this.transformer = transformer;
        entryWriter = new AtomEntryFormatWriter(serviceDocument);
        feedWriter = new AtomFeedFormatWriter(serviceDocument);
        entityEntryWriter = new AtomEntityEntryFormatWriter(serviceDocument, metadata);
        this.uriInfo = new UriInfoImpl(uriInfo);
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        if (mediaType.isCompatible(MediaType.APPLICATION_ATOM_XML_TYPE)
                // good old Microsoft Excel 2013 has forced us to need to accept this media type (which is completely wrong) https://github.com/temenostech/IRIS/issues/154
                || mediaType.equals(ExtendedMediaTypes.APPLICATION_ATOMSVC_XML_TYPE)
                || mediaType.equals(MediaType.APPLICATION_XML_TYPE)) {
            return ResourceTypeHelper.isType(type, genericType, EntityResource.class)
                    || ResourceTypeHelper.isType(type, genericType, CollectionResource.class, OEntity.class)
                    || ResourceTypeHelper.isType(type, genericType, CollectionResource.class, Entity.class);
        }
        return false;
    }

    @Override
    public long getSize(RESTResource t, Class<?> type, Type genericType, Annotation[] annotations,
            MediaType mediaType) {
        return -1;
    }

    /**
     * Writes a Atom (OData) representation of {@link EntityResource} to the output stream.
     * 
     * @precondition supplied {@link EntityResource} is non null
     * @precondition {@link EntityResource#getEntity()} returns a valid OEntity, this 
     * provider only supports serialising OEntities
     * @postcondition non null Atom (OData) XML document written to OutputStream
     * @invariant valid OutputStream
     */
    @SuppressWarnings("unchecked")
    @Override
    public void writeTo(RESTResource resource, Class<?> type, Type genericType, Annotation[] annotations,
            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
            throws IOException, WebApplicationException {
        assert resource != null;
        assert uriInfo != null;
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        RESTResource restResource = processLinks((RESTResource) resource);
        Collection<Link> processedLinks = restResource.getLinks();
        if (ResourceTypeHelper.isType(type, genericType, EntityResource.class, OEntity.class)) {
            EntityResource<OEntity> entityResource = (EntityResource<OEntity>) resource;
            OEntity tempEntity = entityResource.getEntity();
            EdmEntitySet entitySet = getEdmEntitySet(entityResource.getEntityName());
            List<OLink> olinks = formOLinks(entityResource);
            //Write entry
            // create OEntity with our EdmEntitySet see issue https://github.com/aphethean/IRIS/issues/20
            OEntity oentity = OEntities.create(entitySet, tempEntity.getEntityKey(), tempEntity.getProperties(),
                    null);
            entryWriter.write(uriInfo, new OutputStreamWriter(buffer, UTF_8), Responses.entity(oentity), entitySet,
                    olinks);
        } else if (ResourceTypeHelper.isType(type, genericType, EntityResource.class, Entity.class)) {
            EntityResource<Entity> entityResource = (EntityResource<Entity>) resource;
            //Write entry
            Entity entity = entityResource.getEntity();
            String entityName = entityResource.getEntityName();
            // Write Entity object with Abdera implementation
            entityEntryWriter.write(uriInfo, new OutputStreamWriter(buffer, UTF_8), entityName, entity,
                    processedLinks, entityResource.getEmbedded());
        } else if (ResourceTypeHelper.isType(type, genericType, EntityResource.class)) {
            EntityResource<Object> entityResource = (EntityResource<Object>) resource;
            //Links and entity properties
            Object entity = entityResource.getEntity();
            String entityName = entityResource.getEntityName();
            EntityProperties props = new EntityProperties();
            if (entity != null) {
                Map<String, Object> objProps = (transformer != null ? transformer : new BeanTransformer())
                        .transform(entity);
                if (objProps != null) {
                    for (String propName : objProps.keySet()) {
                        props.setProperty(new EntityProperty(propName, objProps.get(propName)));
                    }
                }
            }
            entityEntryWriter.write(uriInfo, new OutputStreamWriter(buffer, UTF_8), entityName,
                    new Entity(entityName, props), processedLinks, entityResource.getEmbedded());
        } else if (ResourceTypeHelper.isType(type, genericType, CollectionResource.class, OEntity.class)) {
            CollectionResource<OEntity> collectionResource = ((CollectionResource<OEntity>) resource);
            EdmEntitySet entitySet = getEdmEntitySet(collectionResource.getEntityName());
            List<EntityResource<OEntity>> collectionEntities = (List<EntityResource<OEntity>>) collectionResource
                    .getEntities();
            List<OEntity> entities = new ArrayList<OEntity>();
            Map<OEntity, Collection<Link>> linkId = new HashMap<OEntity, Collection<Link>>();
            for (EntityResource<OEntity> collectionEntity : collectionEntities) {
                // create OEntity with our EdmEntitySet see issue https://github.com/aphethean/IRIS/issues/20
                OEntity tempEntity = collectionEntity.getEntity();
                List<OLink> olinks = formOLinks(collectionEntity);
                Collection<Link> links = collectionEntity.getLinks();
                OEntity entity = OEntities.create(entitySet, null, tempEntity.getEntityKey(),
                        tempEntity.getEntityTag(), tempEntity.getProperties(), olinks);
                entities.add(entity);
                linkId.put(entity, links);
            }
            // TODO implement collection properties and get transient value for skiptoken
            Integer inlineCount = collectionResource.getInlineCount();
            String skipToken = null;
            feedWriter.write(uriInfo, new OutputStreamWriter(buffer, UTF_8), processedLinks,
                    Responses.entities(entities, entitySet, inlineCount, skipToken), metadata.getModelName(),
                    linkId);
        } else if (ResourceTypeHelper.isType(type, genericType, CollectionResource.class, Entity.class)) {
            CollectionResource<Entity> collectionResource = ((CollectionResource<Entity>) resource);

            // TODO implement collection properties and get transient value for skiptoken
            Integer inlineCount = collectionResource.getInlineCount();
            String skipToken = null;
            //Write feed
            AtomEntityFeedFormatWriter entityFeedWriter = new AtomEntityFeedFormatWriter(serviceDocument, metadata);
            entityFeedWriter.write(uriInfo, new OutputStreamWriter(buffer, UTF_8), collectionResource, inlineCount,
                    skipToken, metadata.getModelName());
        } else {
            LOGGER.error("Accepted object for writing in isWriteable, but type not supported in writeTo method");
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }

        //Set response headers
        if (httpHeaders != null) {
            httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_ATOM_XML); //Workaround for https://issues.apache.org/jira/browse/WINK-374
            httpHeaders.putSingle(HttpHeaders.CONTENT_LENGTH, Integer.toString(buffer.size()));
        }
        IOUtils.copy(new ByteArrayInputStream(buffer.toByteArray()), entityStream);
    }

    public RESTResource processLinks(RESTResource restResource) {
        Collection<Link> linksCollection = restResource.getLinks();
        List<Link> processedLinks = new ArrayList<Link>();
        if (linksCollection != null) {
            for (Link linkToAdd : linksCollection) {
                Link link = linkInterceptor.addingLink(restResource, linkToAdd);
                if (link != null) {
                    processedLinks.add(link);
                }
            }
        }
        restResource.setLinks(processedLinks);

        // process embedded resources
        if (restResource.getEmbedded() != null) {
            Map<Transition, RESTResource> embeddedResources = restResource.getEmbedded();
            for (RESTResource embeddedResource : embeddedResources.values()) {
                processLinks(embeddedResource);
            }
        }

        // process entities in collection resource
        if (restResource instanceof CollectionResource) {
            CollectionResource<?> collectionResource = (CollectionResource<?>) restResource;
            for (EntityResource<?> entityResource : collectionResource.getEntities()) {
                processLinks(entityResource);
            }
        }
        return restResource;
    }

    public List<OLink> formOLinks(EntityResource<OEntity> entityResource) {
        // Create embedded resources from $expand
        addExpandedLinks(entityResource);

        //Add entity links
        List<OLink> olinks = new ArrayList<OLink>();
        if (entityResource.getLinks() != null && !entityResource.getLinks().isEmpty()) {
            for (Link link : entityResource.getLinks()) {
                addLinkToOLinks(olinks, link, entityResource);
            }
        }

        return olinks;
    }

    /*
     * Using the supplied EntityResource, add the embedded resources
     * from the OEntity embedded resources.  NB - only an OEntity can
     * carry OLinks.
     */
    public void addExpandedLinks(EntityResource<OEntity> entityResource) {
        RequestContext requestContext = RequestContext.getRequestContext();
        Collection<Link> links = entityResource.getLinks();
        if (links != null) {
            OEntity oentity = entityResource.getEntity();
            List<OLink> olinks = oentity.getLinks();
            for (OLink olink : olinks) {
                if (olink.isInline()) {
                    String relid = InternalUtil.getEntityRelId(oentity);
                    String href = relid + "/" + olink.getTitle();
                    for (Link link : links) {
                        String linkHref = link.getHref();
                        if (requestContext != null) {
                            //Extract the transition fragment from the URI path
                            linkHref = link.getRelativeHref(getBaseUri(serviceDocument, uriInfo));
                        }
                        if (href.equals(linkHref)) {
                            if (entityResource.getEmbedded() == null) {
                                entityResource.setEmbedded(new HashMap<Transition, RESTResource>());
                            }
                            if (olink.isCollection()) {
                                List<OEntity> oentities = olink.getRelatedEntities();
                                Collection<EntityResource<OEntity>> entityResources = new ArrayList<EntityResource<OEntity>>();
                                for (OEntity oe : oentities) {
                                    entityResources.add(new EntityResource<OEntity>(oe));
                                }
                                entityResource.getEmbedded().put(link.getTransition(),
                                        new CollectionResource<OEntity>(entityResources));
                            } else {
                                // replace the OLink's on the current entity
                                OEntity inlineOentity = olink.getRelatedEntity();
                                List<OLink> inlineResourceOlinks = formOLinks(
                                        new EntityResource<OEntity>(inlineOentity));
                                OEntity newInlineOentity = OEntities.create(inlineOentity.getEntitySet(),
                                        inlineOentity.getEntityKey(), inlineOentity.getProperties(),
                                        inlineResourceOlinks);
                                entityResource.getEmbedded().put(link.getTransition(),
                                        new EntityResource<OEntity>(newInlineOentity));
                            }
                        }
                    }
                }
            }
        }
    }

    private void addLinkToOLinks(List<OLink> olinks, Link link, RESTResource resource) {
        RequestContext requestContext = RequestContext.getRequestContext();
        assert link != null;
        assert link.getTransition() != null;
        Map<Transition, RESTResource> embeddedResources = resource.getEmbedded();
        String rel = link.getRel();
        String href = link.getHref();
        if (requestContext != null) {
            //Extract the transition fragment from the URI path
            href = link.getRelativeHref(getBaseUri(serviceDocument, uriInfo));
        }
        String title = link.getTitle();
        OLink olink = null;
        Transition linkTransition = link.getTransition();
        if (linkTransition != null) {
            if (embeddedResources != null && embeddedResources.get(linkTransition) != null
                    && embeddedResources.get(linkTransition) instanceof EntityResource) {
                @SuppressWarnings("unchecked")
                EntityResource<OEntity> embeddedResource = (EntityResource<OEntity>) embeddedResources
                        .get(linkTransition);
                // replace the OLink's on the embedded entity
                OEntity newEmbeddedEntity = processOEntity(embeddedResource);
                olink = OLinks.relatedEntityInline(rel, title, href, newEmbeddedEntity);
            } else if (embeddedResources != null && embeddedResources.get(linkTransition) != null
                    && embeddedResources.get(linkTransition) instanceof CollectionResource) {
                @SuppressWarnings("unchecked")
                CollectionResource<OEntity> embeddedCollectionResource = (CollectionResource<OEntity>) embeddedResources
                        .get(linkTransition);
                List<OEntity> entities = new ArrayList<OEntity>();
                for (EntityResource<OEntity> embeddedResource : embeddedCollectionResource.getEntities()) {
                    // replace the OLink's on the embedded entity
                    OEntity newEmbeddedEntity = processOEntity(embeddedResource);
                    entities.add(newEmbeddedEntity);
                }
                olink = OLinks.relatedEntitiesInline(rel, title, href, entities);
            }
            if (olink == null) {
                if (linkTransition.getTarget() instanceof CollectionResourceState) {
                    olink = OLinks.relatedEntities(rel, title, href);
                } else {
                    olink = OLinks.relatedEntity(rel, title, href);
                }
            }
        }
        olinks.add(olink);
    }

    private OEntity processOEntity(EntityResource<OEntity> entityResource) {
        List<OLink> embeddedLinks = formOLinks(entityResource);
        OEntity embeddedEntity = entityResource.getEntity();
        // replace the OLink's on the embedded entity
        OEntity newOEntity = OEntities.create(embeddedEntity.getEntitySet(), embeddedEntity.getEntityKey(),
                embeddedEntity.getProperties(), embeddedLinks);
        return newOEntity;
    }

    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        // this class can only deserialise EntityResource with OEntity
        return ResourceTypeHelper.isType(type, genericType, EntityResource.class);
    }

    /**
     * Reads a Atom (OData) representation of {@link EntityResource} from the input stream.
     * 
     * @precondition {@link InputStream} contains a valid Atom (OData) Entity enclosed in a <resource/> document
     * @postcondition {@link EntityResource} will be constructed and returned.
     * @invariant valid InputStream
     */
    @Override
    public EntityResource<OEntity> readFrom(Class<RESTResource> type, Type genericType, Annotation[] annotations,
            MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
            throws IOException, WebApplicationException {

        // check media type can be handled, isReadable must have been called
        assert ResourceTypeHelper.isType(type, genericType, EntityResource.class);
        assert mediaType.isCompatible(MediaType.APPLICATION_ATOM_XML_TYPE);

        try {
            OEntityKey entityKey = null;

            // work out the entity name using resource path from UriInfo
            String absoluteUri = uriInfo.getAbsolutePath().toString();

            String resourcePath = absoluteUri.substring(uriInfo.getBaseUri().toString().length());

            ResourceState currentState = null;

            try {
                currentState = getCurrentState(serviceDocument, resourcePath);
            } catch (MethodNotAllowedException e1) {
                StringBuilder allowHeader = new StringBuilder();

                Set<String> allowedMethods = new HashSet<String>(e1.getAllowedMethods());
                allowedMethods.add("HEAD");
                allowedMethods.add("OPTIONS");

                for (String method : allowedMethods) {
                    allowHeader.append(method);
                    allowHeader.append(", ");
                }

                Response response = Response.status(405)
                        .header("Allow", allowHeader.toString().substring(0, allowHeader.length() - 2)).build();

                throw new WebApplicationException(e1, response);
            }

            if (currentState == null)
                throw new IllegalStateException("No state found");
            String pathIdParameter = getPathIdParameter(currentState);

            UriInfo tmpUriInfo = new UriInfoImpl(uriInfo);
            String uriPath = uriInfo.getAbsolutePath().toString()
                    .substring(uriInfo.getBaseUri().toString().length());

            if (uriPath.charAt(0) == '/') {
                uriPath = uriPath.substring(1);
            }

            String statePath = currentState.getPath();

            if (statePath.charAt(0) == '/') {
                statePath = statePath.substring(1);
            }

            new URLHelper().extractPathParameters(tmpUriInfo, uriPath.split("/"), statePath.split("/"));

            MultivaluedMap<String, String> pathParameters = tmpUriInfo.getPathParameters();
            if (pathParameters != null && pathParameters.getFirst(pathIdParameter) != null) {
                if (STRING_KEY_RESOURCE_PATTERN.matcher(resourcePath).find()) {
                    entityKey = OEntityKey.create(pathParameters.getFirst(pathIdParameter));
                } else {
                    entityKey = OEntityKey.parse(pathParameters.getFirst(pathIdParameter));
                }
            }

            if (currentState.getEntityName() == null) {
                throw new IllegalStateException("Entity name could not be determined");
            }

            /*
             *  get the entity set name using the metadata
             */
            String entitySetName = getEntitySet(currentState);

            // Check contents of the stream, if empty or null then return empty resource
            InputStream verifiedStream = verifyContentReceieved(entityStream);
            if (verifiedStream == null) {
                return new EntityResource<OEntity>();
            }

            // Lets parse the request content
            Reader reader = new InputStreamReader(verifiedStream);
            assert entitySetName != null : "Must have found a resource or thrown exception";
            Entry e = new AtomEntryFormatParserExt(metadataOData4j, entitySetName, entityKey, null).parse(reader);

            return new EntityResource<OEntity>(e.getEntity());
        } catch (IllegalStateException e) {
            LOGGER.warn("Malformed request from client", e);
            throw new WebApplicationException(Status.BAD_REQUEST);
        }

    }

    /*
     * Find the entity set name for this resource
     */
    public String getEntitySet(ResourceState state) {
        String entitySetName = null;
        String fqTargetEntityName = metadata.getModelName() + Metadata.MODEL_SUFFIX + "." + state.getEntityName();
        try {
            EdmEntityType targetEntityType = (EdmEntityType) getEdmDataService()
                    .findEdmEntityType(fqTargetEntityName);
            if (targetEntityType != null) {
                EdmEntitySet targetEntitySet = getEdmDataService().getEdmEntitySet(targetEntityType);
                if (targetEntitySet != null)
                    entitySetName = targetEntitySet.getName();
            }
        } catch (NotFoundException e) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Failed to get entity set name", e);
            }
        }

        if (entitySetName == null) {
            try {
                entitySetName = getEdmEntitySet(state.getEntityName()).getName();
            } catch (NotFoundException e) {
                LOGGER.warn("Entity [" + fqTargetEntityName + "] is not an entity set.", e);
            }
            if (entitySetName == null) {
                entitySetName = state.getName();
            }
        }
        return entitySetName;
    }

    protected ResourceState getCurrentState(ResourceState serviceDocument, String resourcePath)
            throws MethodNotAllowedException {
        ResourceState state = null;
        if (resourcePath != null) {
            String tmpResourcePath = uriInfo.getAbsolutePath().toString()
                    .replaceFirst(uriInfo.getBaseUri().toString(), "");

            if (tmpResourcePath.charAt(0) != '/') {
                tmpResourcePath = '/' + tmpResourcePath;
            }

            state = resourceStateProvider.getResourceState(requestContext.getMethod(), tmpResourcePath);
        }
        return state;
    }

    /*
     * For a given resource state, get the path parameter used for the id.
     * @param state
     * @return
     */
    private String getPathIdParameter(ResourceState state) {
        String pathIdParameter = InteractionContext.DEFAULT_ID_PATH_ELEMENT;
        if (state.getPathIdParameter() != null) {
            pathIdParameter = state.getPathIdParameter();
        }
        return pathIdParameter;
    }

    /* Ugly testing support :-( */
    void setUriInfo(UriInfo uriInfo) {
        this.uriInfo = uriInfo;

        if (resourceStateProvider instanceof DefaultResourceStateProvider) {
            ((DefaultResourceStateProvider) resourceStateProvider).setUriInfo(uriInfo);
        }
    }

    void setRequestContext(Request request) {
        this.requestContext = request;
    }

    void setResourceStateProvider(ResourceStateProvider resourceStateProvider) {
        this.resourceStateProvider = resourceStateProvider;
    }

    /**
     * Method to verify if receieved stream has content or its empty
     * @param stream Stream to check
     * @return verified stream
     * @throws IOException
     */
    private InputStream verifyContentReceieved(InputStream stream) throws IOException {

        if (stream == null) { // Check if its null
            LOGGER.debug("Request stream received as null");
            return null;
        } else if (stream.markSupported()) { // Check stream supports mark/reset
            // mark() and read the first byte just to check
            stream.mark(1);
            final int bytesRead = stream.read(new byte[1]);
            if (bytesRead != -1) {
                //stream not empty
                stream.reset(); // reset the stream as if untouched
                return stream;
            } else {
                //stream empty
                LOGGER.debug("Request received with empty body");
                return null;
            }
        } else {
            // Panic! this stream does not support mark/reset, try with PushbackInputStream as a last resort
            int bytesRead;
            PushbackInputStream pbs = new PushbackInputStream(stream);
            if ((bytesRead = pbs.read()) != -1) {
                // Contents detected, unread and return
                pbs.unread(bytesRead);
                return pbs;
            } else {
                // Empty stream detected
                LOGGER.debug("Request received with empty body!");
                return null;
            }
        }
    }

    /**
     * Our base uri is the uri to the service document.
     * @param serviceDocument
     * @param uriInfo
     * @return
     */
    public static String getBaseUri(ResourceState serviceDocument, UriInfo uriInfo) {
        String baseUri = uriInfo.getBaseUri().toString();
        if (serviceDocument.getPath() != null) {
            if (baseUri.endsWith("/")) {
                baseUri = baseUri.substring(0, baseUri.lastIndexOf("/"));
            }
            baseUri = baseUri + serviceDocument.getPath();
            String absPath = getAbsolutePath(uriInfo);
            baseUri = HypermediaTemplateHelper.getTemplatedBaseUri(baseUri, absPath);
            if (!baseUri.endsWith("/")) {
                baseUri += "/";
            }
        }
        return baseUri;
    }

    public static String getAbsolutePath(UriInfo uriInfo) {
        return uriInfo.getBaseUri() + uriInfo.getPath();
    }

    private EdmDataServices getEdmDataService() {
        return metadataOData4j.getMetadata();
    }

    /*
     * get edmEntitySet
     */
    public EdmEntitySet getEdmEntitySet(String entityName) {
        EdmEntityType entityType = (EdmEntityType) getEdmDataService().findEdmEntityType(entityName);
        EdmEntitySet entitySet = null;
        try {
            entitySet = getEdmDataService().getEdmEntitySet(entityType);
        } catch (Exception e) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Unable to find entity set for [" + entityName + "]", e);
            }
        }

        if (entitySet == null) {
            return metadataOData4j.getEdmEntitySetByEntityName(entityName);
        }
        return entitySet;
    }
}