org.apache.olingo.server.core.requests.DataRequest.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.olingo.server.core.requests.DataRequest.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.olingo.server.core.requests;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ValueType;
import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmNavigationPropertyBinding;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.commons.core.edm.primitivetype.EdmStream;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.api.deserializer.DeserializerException.MessageKeys;
import org.apache.olingo.server.api.deserializer.ODataDeserializer;
import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
import org.apache.olingo.server.api.serializer.RepresentationType;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.uri.UriHelper;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriInfoCrossjoin;
import org.apache.olingo.server.api.uri.UriInfoResource;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty;
import org.apache.olingo.server.api.uri.UriResourceProperty;
import org.apache.olingo.server.api.uri.UriResourceSingleton;
import org.apache.olingo.server.core.ContentNegotiator;
import org.apache.olingo.server.core.ContentNegotiatorException;
import org.apache.olingo.server.core.ReturnRepresentation;
import org.apache.olingo.server.core.ServiceHandler;
import org.apache.olingo.server.core.ServiceRequest;
import org.apache.olingo.server.core.responses.CountResponse;
import org.apache.olingo.server.core.responses.EntityResponse;
import org.apache.olingo.server.core.responses.EntitySetResponse;
import org.apache.olingo.server.core.responses.NoContentResponse;
import org.apache.olingo.server.core.responses.PrimitiveValueResponse;
import org.apache.olingo.server.core.responses.PropertyResponse;
import org.apache.olingo.server.core.responses.StreamResponse;
import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;

public class DataRequest extends ServiceRequest {
    protected UriResourceEntitySet uriResourceEntitySet;
    private boolean countRequest;
    private UriResourceProperty uriResourceProperty;
    private boolean valueRequest;
    private final LinkedList<UriResourceNavigation> uriNavigations = new LinkedList<UriResourceNavigation>();
    private boolean references;

    private RequestType type;
    private UriResourceSingleton uriResourceSingleton;

    /**
     * This sub-categorizes the request so that code can be simplified
     */
    interface RequestType {
        public boolean allowedMethod();

        public ContentType getResponseContentType() throws ContentNegotiatorException;

        public ContextURL getContextURL(OData odata) throws SerializerException;

        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException;
    }

    public DataRequest(OData odata, ServiceMetadata serviceMetadata) {
        super(odata, serviceMetadata);
    }

    public UriResourceEntitySet getUriResourceEntitySet() {
        return uriResourceEntitySet;
    }

    public void setUriResourceEntitySet(UriResourceEntitySet uriResourceEntitySet) {
        this.uriResourceEntitySet = uriResourceEntitySet;
        this.type = new EntityRequest();
    }

    public void setCrossJoin(UriInfoCrossjoin info) {
        this.type = new CrossJoinRequest(info.getEntitySetNames());
    }

    public boolean isSingleton() {
        return this.uriResourceSingleton != null;
    }

    public boolean isCollection() {
        if (!this.uriNavigations.isEmpty()) {
            return this.uriNavigations.getLast().isCollection();
        }
        return this.uriResourceEntitySet != null && this.uriResourceEntitySet.isCollection();
    }

    public EdmEntitySet getEntitySet() {
        return this.uriResourceEntitySet.getEntitySet();
    }

    public boolean isCountRequest() {
        return countRequest;
    }

    public void setCountRequest(boolean countRequest) {
        this.countRequest = countRequest;
        this.type = new CountRequest();
    }

    public boolean isPropertyRequest() {
        return this.uriResourceProperty != null;
    }

    public boolean isPropertyComplex() {
        return (this.uriResourceProperty instanceof UriResourceComplexProperty);
    }

    public boolean isPropertyStream() {
        if (isPropertyComplex()) {
            return false;
        }
        EdmProperty property = ((UriResourcePrimitiveProperty) this.uriResourceProperty).getProperty();
        return (property.getType() instanceof EdmStream);
    }

    public UriResourceProperty getUriResourceProperty() {
        return uriResourceProperty;
    }

    public void setUriResourceProperty(UriResourceProperty uriResourceProperty) {
        this.uriResourceProperty = uriResourceProperty;
        this.type = new PropertyRequest();
    }

    public LinkedList<UriResourceNavigation> getNavigations() {
        return this.uriNavigations;
    }

    public void addUriResourceNavigation(UriResourceNavigation uriResourceNavigation) {
        this.uriNavigations.add(uriResourceNavigation);
    }

    public UriResourceSingleton getUriResourceSingleton() {
        return this.uriResourceSingleton;
    }

    public void setUriResourceSingleton(UriResourceSingleton info) {
        this.uriResourceSingleton = info;
        this.type = new SingletonRequest();
    }

    public List<UriParameter> getKeyPredicates() {
        if (this.uriResourceEntitySet != null) {
            return this.uriResourceEntitySet.getKeyPredicates();
        }
        return null;
    }

    public boolean isReferenceRequest() {
        return this.references;
    }

    public void setReferenceRequest(boolean ref) {
        this.references = ref;
        this.type = new ReferenceRequest();
    }

    public boolean isValueRequest() {
        return valueRequest;
    }

    private boolean hasMediaStream() {
        return this.uriResourceEntitySet != null && this.uriResourceEntitySet.getEntityType().hasStream();
    }

    private InputStream getMediaStream() {
        return this.request.getBody();
    }

    public void setValueRequest(boolean valueRequest) {
        this.valueRequest = valueRequest;
        this.type = new ValueRequest();
    }

    @Override
    public boolean allowedMethod() {
        return this.type.allowedMethod();
    }

    public ContextURL getContextURL(OData odata) throws SerializerException {
        return type.getContextURL(odata);
    }

    @Override
    public void execute(ServiceHandler handler, ODataResponse response)
            throws ODataLibraryException, ODataApplicationException {

        if (!this.type.allowedMethod()) {
            methodNotAllowed();
        }

        this.type.execute(handler, response);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getSerializerOptions(Class<T> serilizerOptions, ContextURL contextUrl, boolean references)
            throws ContentNegotiatorException {
        if (serilizerOptions.isAssignableFrom(PrimitiveSerializerOptions.class)) {
            return (T) PrimitiveSerializerOptions.with().contextURL(contextUrl)
                    .facetsFrom(getUriResourceProperty().getProperty()).build();
        }
        return super.getSerializerOptions(serilizerOptions, contextUrl, references);
    }

    @Override
    public ContentType getResponseContentType() throws ContentNegotiatorException {
        return type.getResponseContentType();
    }

    class EntityRequest implements RequestType {

        @Override
        public boolean allowedMethod() {
            // the create/update/delete to navigation property is done through references
            // see # 11.4.6
            if (!getNavigations().isEmpty() && !isGET()) {
                return false;
            }

            if ((isGET() || isDELETE()) && getReturnRepresentation() != ReturnRepresentation.NONE) {
                return false;
            }

            // in update, delete entity cases, predicate must be there
            if ((isPATCH() || isPUT() || isDELETE())
                    && (getKeyPredicates() == null || getKeyPredicates().isEmpty())) {
                return false;
            }
            return true;
        }

        @Override
        public ContentType getResponseContentType() throws ContentNegotiatorException {
            return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
                    getCustomContentTypeSupport(),
                    isCollection() ? RepresentationType.COLLECTION_ENTITY : RepresentationType.ENTITY);
        }

        @Override
        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException {

            EntityResponse entityResponse = EntityResponse.getInstance(DataRequest.this, getContextURL(odata),
                    false, response);

            if (isGET()) {
                if (isCollection()) {
                    handler.read(DataRequest.this,
                            EntitySetResponse.getInstance(DataRequest.this, getContextURL(odata), false, response));
                } else {
                    handler.read(DataRequest.this, entityResponse);
                }
            } else if (isPUT() || isPATCH()) {
                // RFC 2616: he result of a request having both an If-Match header field and either
                // an If-None-Match or an If-Modified-Since header fields is undefined
                // by this specification.
                boolean ifMatch = getHeader(HttpHeader.IF_MATCH) != null;
                boolean ifNoneMatch = (getHeader(HttpHeader.IF_NONE_MATCH) != null
                        && getHeader(HttpHeader.IF_NONE_MATCH).equals("*"));
                if (ifMatch) {
                    handler.updateEntity(DataRequest.this, getEntityFromClient(), isPATCH(), getETag(),
                            entityResponse);
                } else if (ifNoneMatch) {
                    // 11.4.4
                    entityResponse = EntityResponse.getInstance(DataRequest.this, getContextURL(odata), false,
                            response, getReturnRepresentation());
                    handler.createEntity(DataRequest.this, getEntityFromClient(), entityResponse);
                } else {
                    handler.upsertEntity(DataRequest.this, getEntityFromClient(), isPATCH(), getETag(),
                            entityResponse);
                }
            } else if (isPOST()) {
                entityResponse = EntityResponse.getInstance(DataRequest.this, getContextURL(odata), false, response,
                        getReturnRepresentation());
                handler.createEntity(DataRequest.this, getEntityFromClient(), entityResponse);
            } else if (isDELETE()) {
                handler.deleteEntity(DataRequest.this, getETag(), entityResponse);
            }
        }

        private Entity getEntityFromClient() throws DeserializerException {
            ODataDeserializer deserializer = odata.createDeserializer(getRequestContentType());
            return deserializer.entity(getODataRequest().getBody(), getEntitySet().getEntityType()).getEntity();
        }

        @Override
        public ContextURL getContextURL(OData odata) throws SerializerException {
            // EntitySet based return
            final UriHelper helper = odata.createUriHelper();
            ContextURL.Builder builder = buildEntitySetContextURL(helper, getEntitySet(), getKeyPredicates(),
                    getUriInfo(), getNavigations(), isCollection(), false);
            return builder.build();
        }
    }

    class CountRequest implements RequestType {

        @Override
        public boolean allowedMethod() {
            if (getReturnRepresentation() != ReturnRepresentation.NONE) {
                return false;
            }

            return isGET();
        }

        @Override
        public ContentType getResponseContentType() throws ContentNegotiatorException {
            return ContentType.TEXT_PLAIN;
        }

        @Override
        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException {
            handler.read(DataRequest.this, CountResponse.getInstance(DataRequest.this, response));
        }

        @Override
        public ContextURL getContextURL(OData odata) throws SerializerException {
            return null;
        }
    }

    /**
     * Is NavigationProperty Reference.
     */
    class ReferenceRequest implements RequestType {

        @Override
        public boolean allowedMethod() {
            if ((isGET() || isDELETE()) && getReturnRepresentation() != ReturnRepresentation.NONE) {
                return false;
            }

            // references are only allowed on the navigation properties
            if (getNavigations().isEmpty()) {
                return false;
            }

            // 11.4.6.1 - post allowed on only collection valued navigation
            if (isPOST() && !getNavigations().getLast().isCollection()) {
                return false;
            }

            // 11.4.6.3 - PUT allowed on single valued navigation
            if (isPUT() && getNavigations().getLast().isCollection()) {
                return false;
            }

            // No defined behavior in spec
            if (isPATCH()) {
                return false;
            }

            return true;
        }

        @Override
        public ContentType getResponseContentType() throws ContentNegotiatorException {
            return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
                    getCustomContentTypeSupport(),
                    isCollection() ? RepresentationType.COLLECTION_REFERENCE : RepresentationType.REFERENCE);
        }

        @Override
        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException {
            if (isGET()) {
                if (isCollection()) {
                    handler.read(DataRequest.this,
                            EntitySetResponse.getInstance(DataRequest.this, getContextURL(odata), true, response));
                } else {
                    handler.read(DataRequest.this,
                            EntityResponse.getInstance(DataRequest.this, getContextURL(odata), true, response));
                }
            } else if (isDELETE()) {
                // if this against the collection, user need to look at $id param for entity ref #11.4.6.2
                String id = getQueryParameter("$id");
                if (id == null && isCollection()) {
                    throw new UriParserSemanticException("$id not specified for delete of reference",
                            UriParserSemanticException.MessageKeys.RESOURCE_NOT_FOUND);
                }
                if (id == null) {
                    handler.deleteReference(DataRequest.this, null, getETag(),
                            new NoContentResponse(getServiceMetaData(), response));
                } else {
                    try {
                        handler.deleteReference(DataRequest.this, new URI(id), getETag(),
                                new NoContentResponse(getServiceMetaData(), response));
                    } catch (URISyntaxException e) {
                        throw new DeserializerException("failed to read $id", e, MessageKeys.UNKNOWN_CONTENT);
                    }
                }
            } else if (isPUT()) {
                // note this is always against single reference
                handler.updateReference(DataRequest.this, getETag(), getPayload().get(0),
                        new NoContentResponse(getServiceMetaData(), response));
            } else if (isPOST()) {
                // this needs to be against collection of references
                handler.addReference(DataRequest.this, getETag(), getPayload().get(0),
                        new NoContentResponse(getServiceMetaData(), response));
            }
        }

        // http://docs.oasis-open.org/odata/odata-json-format/v4.0/errata02/os
        // /odata-json-format-v4.0-errata02-os-complete.html#_Toc403940643
        // The below code reads as property and converts to an URI
        private List<URI> getPayload() throws DeserializerException {
            ODataDeserializer deserializer = odata.createDeserializer(getRequestContentType());
            return deserializer.entityReferences(getODataRequest().getBody()).getEntityReferences();
        }

        @Override
        public ContextURL getContextURL(OData odata) throws SerializerException {
            ContextURL.Builder builder = ContextURL.with().suffix(Suffix.REFERENCE);
            if (isCollection()) {
                builder.asCollection();
            }
            return builder.build();
        }
    }

    class PropertyRequest implements RequestType {

        @Override
        public boolean allowedMethod() {
            if ((isGET() || isDELETE() || isPropertyStream())
                    && getReturnRepresentation() != ReturnRepresentation.NONE) {
                return false;
            }

            // create of properties is not allowed,
            // only read, update, delete. Note that delete is
            // same as update with null
            if (isPOST()) {
                return false;
            }

            // 11.4.9.4, collection properties are not supported with merge
            if (isPATCH() && (isCollection() || isPropertyStream())) {
                return false;
            }
            return true;
        }

        @Override
        public ContentType getResponseContentType() throws ContentNegotiatorException {
            if (isPropertyComplex()) {
                return ContentNegotiator.doContentNegotiation(getUriInfo().getFormatOption(), getODataRequest(),
                        getCustomContentTypeSupport(),
                        isCollection() ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX);
            } else if (isPropertyStream()) {
                return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
                        getCustomContentTypeSupport(), RepresentationType.BINARY);
            }
            return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
                    getCustomContentTypeSupport(),
                    isCollection() ? RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE);
        }

        @Override
        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException {

            EdmProperty edmProperty = getUriResourceProperty().getProperty();

            if (isGET()) {
                if (isPropertyStream()) {
                    handler.read(DataRequest.this, new StreamResponse(getServiceMetaData(), response));
                } else {
                    handler.read(DataRequest.this, buildResponse(response, edmProperty));
                }
            } else if (isPATCH()) {
                handler.updateProperty(DataRequest.this, getPropertyValueFromClient(edmProperty), true, getETag(),
                        buildResponse(response, edmProperty));
            } else if (isPUT()) {
                if (isPropertyStream()) {
                    handler.upsertStreamProperty(DataRequest.this, getETag(), request.getBody(),
                            new NoContentResponse(getServiceMetaData(), response));
                } else {
                    handler.updateProperty(DataRequest.this, getPropertyValueFromClient(edmProperty), false,
                            getETag(), buildResponse(response, edmProperty));
                }
            } else if (isDELETE()) {
                if (isPropertyStream()) {
                    handler.upsertStreamProperty(DataRequest.this, getETag(), null,
                            new NoContentResponse(getServiceMetaData(), response));
                } else {
                    Property property = new Property(
                            edmProperty.getType().getFullQualifiedName().getFullQualifiedNameAsString(),
                            edmProperty.getName());
                    handler.updateProperty(DataRequest.this, property, false, getETag(),
                            buildResponse(response, edmProperty));
                }
            }
        }

        private PropertyResponse buildResponse(ODataResponse response, EdmProperty edmProperty)
                throws ContentNegotiatorException, SerializerException {
            PropertyResponse propertyResponse = PropertyResponse.getInstance(DataRequest.this, response,
                    edmProperty.getType(), getContextURL(odata), edmProperty.isCollection());
            return propertyResponse;
        }

        @Override
        public ContextURL getContextURL(OData odata) throws SerializerException {
            final UriHelper helper = odata.createUriHelper();
            EdmProperty edmProperty = getUriResourceProperty().getProperty();

            ContextURL.Builder builder = ContextURL.with()
                    .entitySetOrSingletonOrType(getTargetEntitySet(getEntitySet(), getNavigations()));
            builder.keyPath(helper.buildContextURLKeyPredicate(getUriResourceEntitySet().getKeyPredicates()));
            String navPath = buildNavPath(helper, getEntitySet().getEntityType(), getNavigations(), true);
            if (navPath != null && !navPath.isEmpty()) {
                builder.navOrPropertyPath(navPath + "/" + edmProperty.getName());
            } else {
                builder.navOrPropertyPath(edmProperty.getName());
            }
            if (isPropertyComplex()) {
                EdmComplexType type = ((UriResourceComplexProperty) uriResourceProperty).getComplexType();
                String select = helper.buildContextURLSelectList(type, getUriInfo().getExpandOption(),
                        getUriInfo().getSelectOption());
                builder.selectList(select);
            }
            return builder.build();
        }
    }

    class ValueRequest extends PropertyRequest {

        @Override
        public boolean allowedMethod() {
            //part2-url-conventions # 4.7
            // Properties of type Edm.Stream already return the raw value of 
            // the media stream and do not support appending the $value segment.
            if (isPropertyStream() && isGET()) {
                return false;
            }

            if ((isGET() || isDELETE() || isPropertyStream())
                    && getReturnRepresentation() != ReturnRepresentation.NONE) {
                return false;
            }

            return isGET() || isDELETE() || isPUT();
        }

        @Override
        public ContentType getResponseContentType() throws ContentNegotiatorException {
            RepresentationType valueRepresentationType = uriResourceProperty.getType() == EdmPrimitiveTypeFactory
                    .getInstance(EdmPrimitiveTypeKind.Binary) ? RepresentationType.BINARY
                            : RepresentationType.VALUE;
            return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
                    getCustomContentTypeSupport(), valueRepresentationType);
        }

        @Override
        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException {
            EdmProperty edmProperty = getUriResourceProperty().getProperty();
            if (isGET()) {
                handler.read(DataRequest.this, PrimitiveValueResponse.getInstance(DataRequest.this, response,
                        isCollection(), getUriResourceProperty().getProperty()));
            } else if (isDELETE()) {
                Property property = new Property(
                        edmProperty.getType().getFullQualifiedName().getFullQualifiedNameAsString(),
                        edmProperty.getName());
                PropertyResponse propertyResponse = PropertyResponse.getInstance(DataRequest.this, response,
                        edmProperty.getType(), getContextURL(odata), edmProperty.isCollection());
                handler.updateProperty(DataRequest.this, property, false, getETag(), propertyResponse);
            } else if (isPUT()) {
                PropertyResponse propertyResponse = PropertyResponse.getInstance(DataRequest.this, response,
                        edmProperty.getType(), getContextURL(odata), edmProperty.isCollection());
                Property property = new Property(
                        edmProperty.getType().getFullQualifiedName().getFullQualifiedNameAsString(),
                        edmProperty.getName());
                property.setValue(ValueType.PRIMITIVE, getRawValueFromClient(edmProperty));
                handler.updateProperty(DataRequest.this, property, false, getETag(), propertyResponse);
            }
        }

        @Override
        public ContextURL getContextURL(OData odata) throws SerializerException {
            return null;
        }
    }

    class SingletonRequest implements RequestType {

        @Override
        public boolean allowedMethod() {
            return isGET();
        }

        @Override
        public ContentType getResponseContentType() throws ContentNegotiatorException {
            return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
                    getCustomContentTypeSupport(), RepresentationType.ENTITY);
        }

        @Override
        public ContextURL getContextURL(OData odata) throws SerializerException {
            final UriHelper helper = odata.createUriHelper();
            ContextURL.Builder builder = buildEntitySetContextURL(helper, uriResourceSingleton.getSingleton(), null,
                    getUriInfo(), getNavigations(), isCollection(), true);
            return builder.build();
        }

        @Override
        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException {
            handler.read(DataRequest.this,
                    EntityResponse.getInstance(DataRequest.this, getContextURL(odata), false, response));
        }
    }

    class CrossJoinRequest implements RequestType {
        private final List<String> entitySetNames;

        public CrossJoinRequest(List<String> entitySetNames) {
            this.entitySetNames = entitySetNames;
        }

        @Override
        public boolean allowedMethod() {
            return isGET();
        }

        @Override
        public ContentType getResponseContentType() throws ContentNegotiatorException {
            return ContentNegotiator.doContentNegotiation(getUriInfo().getFormatOption(), getODataRequest(),
                    getCustomContentTypeSupport(), RepresentationType.COLLECTION_COMPLEX);
        }

        @Override
        public void execute(ServiceHandler handler, ODataResponse response)
                throws ODataLibraryException, ODataApplicationException {
            handler.crossJoin(DataRequest.this, this.entitySetNames, response);
        }

        @Override
        public ContextURL getContextURL(OData odata) throws SerializerException {
            ContextURL.Builder builder = ContextURL.with().asCollection();
            return builder.build();
        }
    }

    private org.apache.olingo.commons.api.data.Property getPropertyValueFromClient(EdmProperty edmProperty)
            throws DeserializerException {
        ODataDeserializer deserializer = odata.createDeserializer(getRequestContentType());
        return deserializer.property(getODataRequest().getBody(), edmProperty).getProperty();
    }

    private Object getRawValueFromClient(EdmProperty edmProperty) throws DeserializerException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
        byte[] buffer = new byte[1024];
        int read = 0;
        do {
            try {
                read = IOUtils.read(getODataRequest().getBody(), buffer, 0, 1024);
                bos.write(buffer, 0, read);
                if (read < 1024) {
                    break;
                }
            } catch (IOException e) {
                new DeserializerException("Error reading raw value", SerializerException.MessageKeys.IO_EXCEPTION);
            }
        } while (true);
        return bos.toByteArray();
    }

    static ContextURL.Builder buildEntitySetContextURL(UriHelper helper, EdmBindingTarget edmEntitySet,
            List<UriParameter> keyPredicates, UriInfo uriInfo, LinkedList<UriResourceNavigation> navigations,
            boolean collectionReturn, boolean singleton) throws SerializerException {

        ContextURL.Builder builder = ContextURL.with()
                .entitySetOrSingletonOrType(getTargetEntitySet(edmEntitySet, navigations));
        String select = helper.buildContextURLSelectList(edmEntitySet.getEntityType(), uriInfo.getExpandOption(),
                uriInfo.getSelectOption());
        if (!singleton) {
            builder.suffix(collectionReturn ? null : Suffix.ENTITY);
        }

        builder.selectList(select);

        final UriInfoResource resource = uriInfo.asUriInfoResource();
        final List<UriResource> resourceParts = resource.getUriResourceParts();
        final List<String> path = getPropertyPath(resourceParts);
        String propertyPath = buildPropertyPath(path);
        final String navPath = buildNavPath(helper, edmEntitySet.getEntityType(), navigations, collectionReturn);
        if (navPath != null && !navPath.isEmpty()) {
            if (keyPredicates != null) {
                builder.keyPath(helper.buildContextURLKeyPredicate(keyPredicates));
            }
            if (propertyPath != null) {
                propertyPath = navPath + "/" + propertyPath;
            } else {
                propertyPath = navPath;
            }
        }
        builder.navOrPropertyPath(propertyPath);
        return builder;
    }

    private static List<String> getPropertyPath(final List<UriResource> path) {
        List<String> result = new LinkedList<String>();
        int index = 1;
        while (index < path.size() && path.get(index) instanceof UriResourceProperty) {
            result.add(((UriResourceProperty) path.get(index)).getProperty().getName());
            index++;
        }
        return result;
    }

    private static String buildPropertyPath(final List<String> path) {
        StringBuilder result = new StringBuilder();
        for (final String segment : path) {
            result.append(result.length() == 0 ? "" : '/').append(segment); //$NON-NLS-1$
        }
        return result.length() == 0 ? null : result.toString();
    }

    static String getTargetEntitySet(EdmBindingTarget root, LinkedList<UriResourceNavigation> navigations) {
        EdmEntityType type = root.getEntityType();
        EdmBindingTarget targetEntitySet = root;
        String targetEntitySetName = root.getName();
        String name = null;
        for (UriResourceNavigation nav : navigations) {
            name = nav.getProperty().getName();
            EdmNavigationProperty property = type.getNavigationProperty(name);
            if (property.containsTarget()) {
                return root.getName();
            }
            type = nav.getProperty().getType();

            for (EdmNavigationPropertyBinding enb : targetEntitySet.getNavigationPropertyBindings()) {
                if (enb.getPath().equals(name)) {
                    targetEntitySetName = enb.getTarget();
                } else if (enb.getPath().endsWith("/" + name)) {
                    targetEntitySetName = enb.getTarget();
                }
            }
        }
        return targetEntitySetName;
    }

    static String buildNavPath(UriHelper helper, EdmEntityType rootType,
            LinkedList<UriResourceNavigation> navigations, boolean includeLastPredicates)
            throws SerializerException {
        if (navigations.isEmpty()) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        boolean containsTarget = false;
        EdmEntityType type = rootType;
        for (UriResourceNavigation nav : navigations) {
            String name = nav.getProperty().getName();
            EdmNavigationProperty property = type.getNavigationProperty(name);
            if (property.containsTarget()) {
                containsTarget = true;
            }
            type = nav.getProperty().getType();
        }

        if (containsTarget) {
            for (int i = 0; i < navigations.size(); i++) {
                UriResourceNavigation nav = navigations.get(i);
                if (i > 0) {
                    sb.append("/");
                }
                sb.append(nav.getProperty().getName());

                boolean skipKeys = false;
                if (navigations.size() == i + 1 && !includeLastPredicates) {
                    skipKeys = true;
                }

                if (!skipKeys && !nav.getKeyPredicates().isEmpty()) {
                    sb.append("(");
                    sb.append(helper.buildContextURLKeyPredicate(nav.getKeyPredicates()));
                    sb.append(")");
                }

                if (nav.getTypeFilterOnCollection() != null) {
                    sb.append("/").append(
                            nav.getTypeFilterOnCollection().getFullQualifiedName().getFullQualifiedNameAsString());
                } else if (nav.getTypeFilterOnEntry() != null) {
                    sb.append("/").append(
                            nav.getTypeFilterOnEntry().getFullQualifiedName().getFullQualifiedNameAsString());
                }
            }
        }
        return sb.toString();
    }
}