diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ComplexTypeCollectionProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ComplexTypeCollectionProcessor.java new file mode 100644 index 000000000..42f676bf9 --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ComplexTypeCollectionProcessor.java @@ -0,0 +1,46 @@ +/* + * 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.api.processor; + +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; + +/** + * Processor interface for handling a collection of complex-type instances, e.g., + * a property of an entity defined as collection of complex-type instances. + */ +public interface ComplexTypeCollectionProcessor extends Processor { + + /** + * Reads complex-type collection. + * If it is not available, for example due to permissions, the service responds with 404 Not Found. + * @param request OData request object containing raw HTTP information + * @param response OData response object for collecting response data + * @param uriInfo information of a parsed OData URI + * @param format requested content type after content negotiation + * @throws ODataApplicationException if the service implementation encounters a failure + * @throws SerializerException if serialization failed + */ + void readComplexTypeCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format) + throws ODataApplicationException, SerializerException; +} diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ComplexTypeProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ComplexTypeProcessor.java new file mode 100644 index 000000000..05f9c3daf --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ComplexTypeProcessor.java @@ -0,0 +1,45 @@ +/* + * 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.api.processor; + +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; + +/** + * Processor interface for handling a complex type, e.g., a complex property of an entity. + */ +public interface ComplexTypeProcessor extends Processor { + + /** + * Reads complex type. + * If it is not available, for example due to permissions, the service responds with 404 Not Found. + * @param request OData request object containing raw HTTP information + * @param response OData response object for collecting response data + * @param uriInfo information of a parsed OData URI + * @param format requested content type after content negotiation + * @throws ODataApplicationException if the service implementation encounters a failure + * @throws SerializerException if serialization failed + */ + void readComplexType(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format) + throws ODataApplicationException, SerializerException; +} diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/CountEntityTypeCollectionProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/CountEntityTypeCollectionProcessor.java new file mode 100644 index 000000000..ecff637ca --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/CountEntityTypeCollectionProcessor.java @@ -0,0 +1,44 @@ +/* + * 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.api.processor; + +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; + +/** + * Processor interface for handling counting a collection of entities, e.g., an Entity Set. + */ +public interface CountEntityTypeCollectionProcessor extends Processor { + + /** + * Counts entities from persistence and puts serialized content and status into the response. + * Response content type is text/plain by default. + * + * @param request - OData request object containing raw http information. + * @param response - OData response object for collecting response data + * @param uriInfo - information of a parsed OData uri + * @throws ODataApplicationException + * @throws SerializerException + */ + void countEntityTypeCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo) + throws ODataApplicationException, SerializerException; +} diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntitySetProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityTypeCollectionProcessor.java similarity index 64% rename from lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntitySetProcessor.java rename to lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityTypeCollectionProcessor.java index fbb3faf0b..71a4a3d47 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntitySetProcessor.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityTypeCollectionProcessor.java @@ -26,9 +26,9 @@ import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriInfo; /** - * Processor interface for handling EntitySets (i.e. collection of entities). + * Processor interface for handling a collection of entities, e.g., an Entity Set. */ -public interface EntitySetProcessor extends Processor { +public interface EntityTypeCollectionProcessor extends Processor { /** * Reads entities data from persistence and puts serialized content and status into the response. @@ -40,19 +40,6 @@ public interface EntitySetProcessor extends Processor { * @throws ODataApplicationException * @throws SerializerException */ - void readEntitySet(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestedContentType) - throws ODataApplicationException, SerializerException; - - /** - * Count entities from persistence and puts serialized content and status into the response. - * Response content type is text/plain by default. - * - * @param request - OData request object containing raw http information. - * @param response - OData response object for collecting response data - * @param uriInfo - information of a parsed OData uri - * @throws ODataApplicationException - * @throws SerializerException - */ - void countEntitySet(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException, - SerializerException; + void readEntityTypeCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, + ContentType requestedContentType) throws ODataApplicationException, SerializerException; } diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityTypeProcessor.java similarity index 87% rename from lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityProcessor.java rename to lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityTypeProcessor.java index 783b3d2dd..28df77467 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityProcessor.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/EntityTypeProcessor.java @@ -26,9 +26,9 @@ import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriInfo; /** - * Processor interface for handling a single Entity. + * Processor interface for handling a single instance of an Entity Type. */ -public interface EntityProcessor extends Processor { +public interface EntityTypeProcessor extends Processor { /** * Reads entity data from persistence and puts serialized content and status into the response. @@ -39,6 +39,6 @@ public interface EntityProcessor extends Processor { * @throws ODataApplicationException * @throws SerializerException */ - void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestedContentType) + void readEntityType(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestedContentType) throws ODataApplicationException, SerializerException; } diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PrimitiveTypeCollectionProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PrimitiveTypeCollectionProcessor.java new file mode 100644 index 000000000..42ccc4b9a --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PrimitiveTypeCollectionProcessor.java @@ -0,0 +1,46 @@ +/* + * 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.api.processor; + +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; + +/** + * Processor interface for handling a collection of primitive-type instances, e.g., + * a property of an entity defined as collection of primitive-type instances. + */ +public interface PrimitiveTypeCollectionProcessor extends Processor { + + /** + * Reads primitive-type collection. + * If it is not available, for example due to permissions, the service responds with 404 Not Found. + * @param request OData request object containing raw HTTP information + * @param response OData response object for collecting response data + * @param uriInfo information of a parsed OData URI + * @param format requested content type after content negotiation + * @throws ODataApplicationException if the service implementation encounters a failure + * @throws SerializerException if serialization failed + */ + void readPrimitiveTypeCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format) + throws ODataApplicationException, SerializerException; +} diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PrimitiveTypeProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PrimitiveTypeProcessor.java new file mode 100644 index 000000000..ec2e81d5d --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PrimitiveTypeProcessor.java @@ -0,0 +1,60 @@ +/* + * 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.api.processor; + +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; + +/** + * Processor interface for handling a primitive type, e.g., a primitive property of an entity. + */ +public interface PrimitiveTypeProcessor extends Processor { + + /** + * Reads primitive type. + * If its value is null, the service responds with 204 No Content. + * If it is not available, for example due to permissions, the service responds with 404 Not Found. + * @param request OData request object containing raw HTTP information + * @param response OData response object for collecting response data + * @param uriInfo information of a parsed OData URI + * @param format requested content type after content negotiation + * @throws ODataApplicationException if the service implementation encounters a failure + * @throws SerializerException if serialization failed + */ + void readPrimitiveType(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format) + throws ODataApplicationException, SerializerException; + + /** + * Reads raw value of a primitive type, e.g., of a primitive property of an entity. + * If the value is null, the service responds with 204 No Content. + * If it is not available, for example due to permissions, the service responds with 404 Not Found. + * @param request OData request object containing raw HTTP information + * @param response OData response object for collecting response data + * @param uriInfo information of a parsed OData URI + * @param format requested content type after content negotiation + * @throws ODataApplicationException if the service implementation encounters a failure + * @throws SerializerException if serialization failed + */ + void readPrimitiveTypeAsValue(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format) + throws ODataApplicationException, SerializerException; +} diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PropertyProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PropertyProcessor.java deleted file mode 100644 index d0d0ee7ed..000000000 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/PropertyProcessor.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.api.processor; - -import org.apache.olingo.commons.api.format.ContentType; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.api.ODataRequest; -import org.apache.olingo.server.api.ODataResponse; -import org.apache.olingo.server.api.serializer.SerializerException; -import org.apache.olingo.server.api.uri.UriInfo; - -/** - * Processor interface for handling a property of an entity. - */ -public interface PropertyProcessor extends Processor { - - /** - * Reads primitive or complex property from entity. - * If the property is single-valued and has the null value, the service responds with 204 No Content. - * If the property is not available, for example due to permissions, the service responds with 404 Not Found - * - * @param request - OData request object containing raw HTTP information - * @param response - OData response object for collecting response data - * @param uriInfo - information of a parsed OData URI - * @param format - requested content type after content negotiation - * @throws ODataApplicationException if service implementation encounters a failure - * @throws SerializerException if serialization failed - */ - void readProperty(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format) - throws ODataApplicationException, SerializerException; - - /** - * Reads value for primitive property from entity. - * If the property is single-valued and has the null value, the service responds with 204 No Content. - * If the property is not available, for example due to permissions, the service responds with 404 Not Found - * - * @param request - OData request object containing raw HTTP information - * @param response - OData response object for collecting response data - * @param uriInfo - information of a parsed OData URI - * @param format - requested content type after content negotiation - * @throws ODataApplicationException if service implementation encounters a failure - * @throws SerializerException if serialization failed - */ - void readPropertyValue(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format) - throws ODataApplicationException, SerializerException; -} diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java index a13ef6fd5..1d90f5550 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java @@ -35,20 +35,24 @@ import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.processor.ComplexTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.ComplexTypeProcessor; +import org.apache.olingo.server.api.processor.CountEntityTypeCollectionProcessor; import org.apache.olingo.server.api.processor.DefaultProcessor; -import org.apache.olingo.server.api.processor.EntitySetProcessor; -import org.apache.olingo.server.api.processor.EntityProcessor; +import org.apache.olingo.server.api.processor.EntityTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityTypeProcessor; import org.apache.olingo.server.api.processor.ExceptionProcessor; import org.apache.olingo.server.api.processor.MetadataProcessor; +import org.apache.olingo.server.api.processor.PrimitiveTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.PrimitiveTypeProcessor; import org.apache.olingo.server.api.processor.Processor; -import org.apache.olingo.server.api.processor.PropertyProcessor; import org.apache.olingo.server.api.processor.ServiceDocumentProcessor; import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; import org.apache.olingo.server.api.serializer.RepresentationType; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriResource; -import org.apache.olingo.server.api.uri.UriResourceKind; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.UriResourcePartTyped; import org.apache.olingo.server.api.uri.UriResourceProperty; @@ -127,11 +131,10 @@ public class ODataHandler { switch (uriInfo.getKind()) { case metadata: if (method.equals(HttpMethod.GET)) { - MetadataProcessor mp = selectProcessor(MetadataProcessor.class); - - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, RepresentationType.METADATA); - mp.readMetadata(request, response, uriInfo, requestedContentType); + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, customContentTypeSupport, RepresentationType.METADATA); + selectProcessor(MetadataProcessor.class) + .readMetadata(request, response, uriInfo, requestedContentType); } else { throw new ODataHandlerException("HttpMethod " + method + " not allowed for metadata document", ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); @@ -140,15 +143,13 @@ public class ODataHandler { case service: if (method.equals(HttpMethod.GET)) { if ("".equals(request.getRawODataPath())) { - RedirectProcessor rdp = selectProcessor(RedirectProcessor.class); - rdp.redirect(request, response); + selectProcessor(RedirectProcessor.class).redirect(request, response); } else { - ServiceDocumentProcessor sdp = selectProcessor(ServiceDocumentProcessor.class); + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, customContentTypeSupport, RepresentationType.SERVICE); - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, RepresentationType.SERVICE); - - sdp.readServiceDocument(request, response, uriInfo, requestedContentType); + selectProcessor(ServiceDocumentProcessor.class) + .readServiceDocument(request, response, uriInfo, requestedContentType); } } else { throw new ODataHandlerException("HttpMethod " + method + " not allowed for service document", @@ -185,113 +186,118 @@ public class ODataHandler { private void handleResourceDispatching(final ODataRequest request, final ODataResponse response) throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, SerializerException { + final HttpMethod method = request.getMethod(); final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1; - UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex); + final UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex); switch (lastPathSegment.getKind()) { case entitySet: - if (((UriResourcePartTyped) lastPathSegment).isCollection()) { - if (request.getMethod().equals(HttpMethod.GET)) { - EntitySetProcessor cp = selectProcessor(EntitySetProcessor.class); - - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, RepresentationType.COLLECTION_ENTITY); - - cp.readEntitySet(request, response, uriInfo, requestedContentType); - } else { - throw new ODataHandlerException("not implemented", - ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); - } - } else { - if (request.getMethod().equals(HttpMethod.GET)) { - EntityProcessor ep = selectProcessor(EntityProcessor.class); - - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, RepresentationType.ENTITY); - - ep.readEntity(request, response, uriInfo, requestedContentType); - } else { - throw new ODataHandlerException("not implemented", - ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); - } - } - break; case navigationProperty: - if (((UriResourceNavigation) lastPathSegment).isCollection()) { - if (request.getMethod().equals(HttpMethod.GET)) { - EntitySetProcessor cp = selectProcessor(EntitySetProcessor.class); + if (((UriResourcePartTyped) lastPathSegment).isCollection()) { + if (method.equals(HttpMethod.GET)) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, customContentTypeSupport, RepresentationType.COLLECTION_ENTITY); - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, RepresentationType.COLLECTION_ENTITY); - - cp.readEntitySet(request, response, uriInfo, requestedContentType); + selectProcessor(EntityTypeCollectionProcessor.class) + .readEntityTypeCollection(request, response, uriInfo, requestedContentType); } else { throw new ODataHandlerException("not implemented", ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); } } else { - if (request.getMethod().equals(HttpMethod.GET)) { - EntityProcessor ep = selectProcessor(EntityProcessor.class); + if (method.equals(HttpMethod.GET)) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, customContentTypeSupport, RepresentationType.ENTITY); - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, RepresentationType.ENTITY); - - ep.readEntity(request, response, uriInfo, requestedContentType); + selectProcessor(EntityTypeProcessor.class) + .readEntityType(request, response, uriInfo, requestedContentType); } else { throw new ODataHandlerException("not implemented", ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); } } break; + case count: - if (request.getMethod().equals(HttpMethod.GET)) { - EntitySetProcessor cp = selectProcessor(EntitySetProcessor.class); - cp.countEntitySet(request, response, uriInfo); + if (method.equals(HttpMethod.GET)) { + final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); + if (resource instanceof UriResourceEntitySet || resource instanceof UriResourceNavigation) { + selectProcessor(CountEntityTypeCollectionProcessor.class) + .countEntityTypeCollection(request, response, uriInfo); + } else { + throw new ODataHandlerException( + "Count of collections of primitive-type or complex-type instances is not implemented.", + ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + } } else { - throw new ODataHandlerException("not implemented", - ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + throw new ODataHandlerException("HTTP method " + method + " is not allowed for count.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); } break; + case primitiveProperty: - case complexProperty: - if (request.getMethod().equals(HttpMethod.GET)) { - PropertyProcessor ep = selectProcessor(PropertyProcessor.class); - + if (method.equals(HttpMethod.GET)) { final UriResourceProperty propertyResource = (UriResourceProperty) lastPathSegment; - final boolean isCollection = propertyResource.isCollection(); - final boolean isComplex = propertyResource.getKind() == UriResourceKind.complexProperty; - final RepresentationType representationType = - isComplex ? isCollection ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX : - isCollection ? RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE; - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, representationType); - - ep.readProperty(request, response, uriInfo, requestedContentType); + final RepresentationType representationType = propertyResource.isCollection() ? + RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE; + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, customContentTypeSupport, representationType); + if (representationType == RepresentationType.PRIMITIVE) { + selectProcessor(PrimitiveTypeProcessor.class) + .readPrimitiveType(request, response, uriInfo, requestedContentType); + } else { + selectProcessor(PrimitiveTypeCollectionProcessor.class) + .readPrimitiveTypeCollection(request, response, uriInfo, requestedContentType); + } } else { throw new ODataHandlerException("not implemented", ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); } break; + + case complexProperty: + if (method.equals(HttpMethod.GET)) { + final UriResourceProperty propertyResource = (UriResourceProperty) lastPathSegment; + final RepresentationType representationType = propertyResource.isCollection() ? + RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX; + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, customContentTypeSupport, representationType); + if (representationType == RepresentationType.COMPLEX) { + selectProcessor(ComplexTypeProcessor.class) + .readComplexType(request, response, uriInfo, requestedContentType); + } else { + selectProcessor(ComplexTypeCollectionProcessor.class) + .readComplexTypeCollection(request, response, uriInfo, requestedContentType); + } + } else { + throw new ODataHandlerException("not implemented", + ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + } + break; + case value: - if (request.getMethod().equals(HttpMethod.GET)) { - PropertyProcessor ep = selectProcessor(PropertyProcessor.class); + if (method.equals(HttpMethod.GET)) { + final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); + if (resource instanceof UriResourceProperty) { + final RepresentationType representationType = + (EdmPrimitiveType) ((UriResourceProperty) resource).getType() == + EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? + RepresentationType.BINARY : RepresentationType.VALUE; + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, customContentTypeSupport, representationType); - final UriResourceProperty propertyResource = - (UriResourceProperty) uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); - final RepresentationType representationType = - (EdmPrimitiveType) propertyResource.getType() == - EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? - RepresentationType.BINARY : - RepresentationType.VALUE; - ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, - customContentTypeSupport, representationType); - - ep.readPropertyValue(request, response, uriInfo, requestedContentType); + selectProcessor(PrimitiveTypeProcessor.class) + .readPrimitiveTypeAsValue(request, response, uriInfo, requestedContentType); + } else { + throw new ODataHandlerException("Media Entity is not implemented.", + ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + } } else { throw new ODataHandlerException("not implemented", ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); } break; + default: throw new ODataHandlerException("not implemented", ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java index 40ceee912..78c9caa9c 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java @@ -24,7 +24,8 @@ import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.edmx.EdmxReference; import org.apache.olingo.server.api.edmx.EdmxReferenceInclude; import org.apache.olingo.server.tecsvc.data.DataProvider; -import org.apache.olingo.server.tecsvc.processor.TechnicalProcessor; +import org.apache.olingo.server.tecsvc.processor.TechnicalEntityProcessor; +import org.apache.olingo.server.tecsvc.processor.TechnicalPrimitiveComplexProcessor; import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,7 +65,8 @@ public class TechnicalServlet extends HttpServlet { } ODataHttpHandler handler = odata.createHandler(serviceMetadata); - handler.register(new TechnicalProcessor(dataProvider)); + handler.register(new TechnicalEntityProcessor(dataProvider)); + handler.register(new TechnicalPrimitiveComplexProcessor(dataProvider)); handler.process(req, resp); } catch (RuntimeException e) { LOG.error("Server Error", e); diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java new file mode 100644 index 000000000..1873629d2 --- /dev/null +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java @@ -0,0 +1,163 @@ +/* + * 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.tecsvc.processor; + +import java.io.ByteArrayInputStream; +import java.util.List; +import java.util.Locale; + +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.EntitySet; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.commons.api.http.HttpContentType; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.processor.CountEntityTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityTypeProcessor; +import org.apache.olingo.server.api.serializer.ODataSerializer; +import org.apache.olingo.server.api.serializer.ODataSerializerOptions; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.queryoption.ExpandOption; +import org.apache.olingo.server.api.uri.queryoption.SelectOption; +import org.apache.olingo.server.tecsvc.data.DataProvider; + +/** + * Technical Processor for entity-related functionality. + */ +public class TechnicalEntityProcessor extends TechnicalProcessor + implements EntityTypeCollectionProcessor, CountEntityTypeCollectionProcessor, EntityTypeProcessor { + + public TechnicalEntityProcessor(final DataProvider dataProvider) { + super(dataProvider); + } + + @Override + public void readEntityTypeCollection(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, + final ContentType requestedContentType) throws ODataApplicationException, SerializerException { + validateOptions(uriInfo.asUriInfoResource()); + if (uriInfo.asUriInfoResource().getUriResourceParts().size() > 1) { + throw new ODataApplicationException("Invalid resource type.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); + final EntitySet entitySet = readEntitySetInternal(edmEntitySet, + uriInfo.getCountOption() != null && uriInfo.getCountOption().getValue()); + if (entitySet == null) { + throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); + } else { + final ODataFormat format = ODataFormat.fromContentType(requestedContentType); + ODataSerializer serializer = odata.createSerializer(format); + final ExpandOption expand = uriInfo.getExpandOption(); + final SelectOption select = uriInfo.getSelectOption(); + response.setContent(serializer.entitySet(edmEntitySet, entitySet, + ODataSerializerOptions.with() + .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : + getContextUrl(serializer, edmEntitySet, false, expand, select)) + .count(uriInfo.getCountOption()) + .expand(expand).select(select) + .build())); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + } + } + + @Override + public void countEntityTypeCollection(final ODataRequest request, ODataResponse response, final UriInfo uriInfo) + throws ODataApplicationException, SerializerException { + validateOptions(uriInfo.asUriInfoResource()); + final List resourceParts = uriInfo.asUriInfoResource().getUriResourceParts(); + final int pos = resourceParts.size() - 2; + if (pos > 0) { + throw new ODataApplicationException("Invalid resource type.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + final EntitySet entitySet = + readEntitySetInternal(((UriResourceEntitySet) resourceParts.get(pos)).getEntitySet(), true); + if (entitySet == null) { + throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); + } else { + response.setContent(new ByteArrayInputStream(entitySet.getCount().toString().getBytes())); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, HttpContentType.TEXT_PLAIN); + } + } + + @Override + public void readEntityType(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, + final ContentType requestedContentType) throws ODataApplicationException, SerializerException { + validateOptions(uriInfo.asUriInfoResource()); + if (uriInfo.asUriInfoResource().getUriResourceParts().size() > 1) { + throw new ODataApplicationException("Invalid resource type.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); + final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0); + final Entity entity = dataProvider.read(edmEntitySet, resourceEntitySet.getKeyPredicates()); + + if (entity == null) { + throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); + } else { + final ODataFormat format = ODataFormat.fromContentType(requestedContentType); + ODataSerializer serializer = odata.createSerializer(format); + final ExpandOption expand = uriInfo.getExpandOption(); + final SelectOption select = uriInfo.getSelectOption(); + response.setContent(serializer.entity(edmEntitySet, entity, + ODataSerializerOptions.with() + .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : + getContextUrl(serializer, edmEntitySet, true, expand, select)) + .count(uriInfo.getCountOption()) + .expand(expand).select(select) + .build())); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + } + } + + private EntitySet readEntitySetInternal(final EdmEntitySet edmEntitySet, final boolean withCount) + throws DataProvider.DataProviderException { + EntitySet entitySet = dataProvider.readAll(edmEntitySet); + // TODO: set count (correctly) and next link + if (withCount && entitySet.getCount() == null) { + entitySet.setCount(entitySet.getEntities().size()); + } + return entitySet; + } + + private ContextURL getContextUrl(final ODataSerializer serializer, + final EdmEntitySet entitySet, final boolean isSingleEntity, + final ExpandOption expand, final SelectOption select) throws SerializerException { + return ContextURL.with().entitySet(entitySet) + .selectList(serializer.buildContextURLSelectList(entitySet, expand, select)) + .suffix(isSingleEntity ? Suffix.ENTITY : null) + .build(); + } +} diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java new file mode 100644 index 000000000..9666f6dac --- /dev/null +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java @@ -0,0 +1,224 @@ +/* + * 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.tecsvc.processor; + +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +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.format.ODataFormat; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.processor.ComplexTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.ComplexTypeProcessor; +import org.apache.olingo.server.api.processor.PrimitiveTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.PrimitiveTypeProcessor; +import org.apache.olingo.server.api.serializer.ODataSerializer; +import org.apache.olingo.server.api.serializer.ODataSerializerOptions; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriInfoResource; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.UriResourceKind; +import org.apache.olingo.server.api.uri.UriResourceProperty; +import org.apache.olingo.server.tecsvc.data.DataProvider; + +/** + * Technical Processor which provides functionality related to primitive and complex types and collections thereof. + */ +public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor + implements PrimitiveTypeProcessor, PrimitiveTypeCollectionProcessor, + ComplexTypeProcessor, ComplexTypeCollectionProcessor { + + public TechnicalPrimitiveComplexProcessor(final DataProvider dataProvider) { + super(dataProvider); + } + + @Override + public void readPrimitiveType(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, + final ContentType contentType) throws ODataApplicationException, SerializerException { + readProperty(response, uriInfo, contentType); + } + + @Override + public void readPrimitiveTypeCollection(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, + final ContentType contentType) throws ODataApplicationException, SerializerException { + readProperty(response, uriInfo, contentType); + } + + @Override + public void readComplexType(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, + final ContentType contentType) throws ODataApplicationException, SerializerException { + readProperty(response, uriInfo, contentType); + } + + @Override + public void readComplexTypeCollection(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, + final ContentType contentType) throws ODataApplicationException, SerializerException { + readProperty(response, uriInfo, contentType); + } + + private void readProperty(ODataResponse response, final UriInfo uriInfo, final ContentType contentType) + throws ODataApplicationException, SerializerException { + final UriInfoResource resource = uriInfo.asUriInfoResource(); + validateOptions(resource); + validatePath(resource); + + final List resourceParts = resource.getUriResourceParts(); + final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) resourceParts.get(0); + final List path = getPropertyPath(resourceParts); + + final Property property = getPropertyData(resourceEntitySet, path); + + if (property == null) { + throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); + } else { + if (property.getValue() == null) { + response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); + } else { + final EdmEntitySet edmEntitySet = getEdmEntitySet(resource); + final EdmProperty edmProperty = ((UriResourceProperty) resourceParts.get(path.size())).getProperty(); + + final ODataFormat format = ODataFormat.fromContentType(contentType); + ODataSerializer serializer = odata.createSerializer(format); + response.setContent(serializer.entityProperty(edmProperty, property, + ODataSerializerOptions.with().contextURL(format == ODataFormat.JSON_NO_METADATA ? null : + ContextURL.with().entitySet(edmEntitySet) + .keyPath(serializer.buildContextURLKeyPredicate( + ((UriResourceEntitySet) resourceParts.get(0)).getKeyPredicates())) + .navOrPropertyPath(buildPropertyPath(path)) + .build()) + .build())); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, contentType.toContentTypeString()); + } + } + } + + private Property getPropertyData(final UriResourceEntitySet resourceEntitySet, final List path) + throws ODataApplicationException { + final Entity entity = dataProvider.read(resourceEntitySet.getEntitySet(), resourceEntitySet.getKeyPredicates()); + if (entity == null) { + throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); + } else { + Property property = entity.getProperty(path.get(0)); + for (final String name : path.subList(1, path.size())) { + if (property != null && (property.isLinkedComplex() || property.isComplex())) { + final List complex = property.isLinkedComplex() ? + property.asLinkedComplex().getValue() : property.asComplex(); + property = null; + for (final Property innerProperty : complex) { + if (innerProperty.getName().equals(name)) { + property = innerProperty; + break; + } + } + } + } + return property; + } + } + + private List getPropertyPath(final List path) { + List result = new LinkedList(); + int index = 1; + while (index < path.size() && path.get(index) instanceof UriResourceProperty) { + result.add(((UriResourceProperty) path.get(index)).getProperty().getName()); + index++; + } + return result; + } + + private String buildPropertyPath(final List path) { + StringBuilder result = new StringBuilder(); + for (final String segment : path) { + result.append(result.length() == 0 ? "" : '/').append(segment); + } + return result.toString(); + } + + @Override + public void readPrimitiveTypeAsValue(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, + final ContentType contentType) throws ODataApplicationException, SerializerException { + final UriInfoResource resource = uriInfo.asUriInfoResource(); + validateOptions(resource); + validatePath(resource); + + final List resourceParts = resource.getUriResourceParts(); + final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) resourceParts.get(0); + final List path = getPropertyPath(resourceParts); + + final Property property = getPropertyData(resourceEntitySet, path); + + if (property == null || property.getValue() == null) { + response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); + } else { + final EdmProperty edmProperty = ((UriResourceProperty) resourceParts.get(path.size())).getProperty(); + final EdmPrimitiveType type = (EdmPrimitiveType) edmProperty.getType(); + if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary)) { + response.setContent(new ByteArrayInputStream((byte[]) property.getValue())); + } else { + try { + final String value = type.valueToString(property.getValue(), + edmProperty.isNullable(), edmProperty.getMaxLength(), + edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode()); + response.setContent(new ByteArrayInputStream(value.getBytes("UTF-8"))); + } catch (final EdmPrimitiveTypeException e) { + throw new ODataApplicationException("Error in value formatting.", + HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e); + } catch (final UnsupportedEncodingException e) { + throw new ODataApplicationException("Encoding exception.", + HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e); + } + } + response.setHeader(HttpHeader.CONTENT_TYPE, contentType.toContentTypeString()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + } + } + + private void validatePath(final UriInfoResource uriInfo) throws ODataApplicationException { + final List resourcePaths = uriInfo.getUriResourceParts(); + for (final UriResource segment : resourcePaths.subList(1, resourcePaths.size())) { + final UriResourceKind kind = segment.getKind(); + if (kind != UriResourceKind.primitiveProperty + && kind != UriResourceKind.complexProperty + && kind != UriResourceKind.count + && kind != UriResourceKind.value) { + throw new ODataApplicationException("Invalid resource type.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + } + } +} diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java index d88a02f91..62c0140db 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java @@ -18,60 +18,29 @@ */ package org.apache.olingo.server.tecsvc.processor; -import java.io.ByteArrayInputStream; -import java.io.UnsupportedEncodingException; -import java.util.LinkedList; import java.util.List; import java.util.Locale; -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.EntitySet; -import org.apache.olingo.commons.api.data.Property; -import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmEntitySet; -import org.apache.olingo.commons.api.edm.EdmPrimitiveType; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; -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.format.ODataFormat; -import org.apache.olingo.commons.api.http.HttpContentType; -import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.api.ODataRequest; -import org.apache.olingo.server.api.ODataResponse; import org.apache.olingo.server.api.ServiceMetadata; -import org.apache.olingo.server.api.processor.EntityProcessor; -import org.apache.olingo.server.api.processor.EntitySetProcessor; -import org.apache.olingo.server.api.processor.PropertyProcessor; -import org.apache.olingo.server.api.serializer.ODataSerializer; -import org.apache.olingo.server.api.serializer.ODataSerializerOptions; -import org.apache.olingo.server.api.serializer.SerializerException; -import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.processor.Processor; 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.UriResourceEntitySet; -import org.apache.olingo.server.api.uri.UriResourceKind; -import org.apache.olingo.server.api.uri.UriResourceProperty; -import org.apache.olingo.server.api.uri.queryoption.ExpandOption; -import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.tecsvc.data.DataProvider; /** - * Technical Processor which provides currently implemented processor functionality. + * Technical Processor base. */ -public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, PropertyProcessor { +public abstract class TechnicalProcessor implements Processor { - private OData odata; - private DataProvider dataProvider; + protected OData odata; + protected DataProvider dataProvider; - public TechnicalProcessor(final DataProvider dataProvider) { + protected TechnicalProcessor(final DataProvider dataProvider) { this.dataProvider = dataProvider; } @@ -80,92 +49,23 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, this.odata = odata; } - @Override - public void readEntitySet(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, - final ContentType requestedContentType) throws ODataApplicationException, SerializerException { - validateOptions(uriInfo.asUriInfoResource()); - - final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); - final EntitySet entitySet = readEntitySetInternal(edmEntitySet, - uriInfo.getCountOption() != null && uriInfo.getCountOption().getValue()); - if (entitySet == null) { - throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); - } else { - final ODataFormat format = ODataFormat.fromContentType(requestedContentType); - ODataSerializer serializer = odata.createSerializer(format); - final ExpandOption expand = uriInfo.getExpandOption(); - final SelectOption select = uriInfo.getSelectOption(); - response.setContent(serializer.entitySet(edmEntitySet, entitySet, - ODataSerializerOptions.with() - .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : - getContextUrl(serializer, edmEntitySet, false, expand, select, null, null)) - .count(uriInfo.getCountOption()) - .expand(expand).select(select) - .build())); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + protected EdmEntitySet getEdmEntitySet(final UriInfoResource uriInfo) throws ODataApplicationException { + final List resourcePaths = uriInfo.getUriResourceParts(); + // first must be entity set + if (!(resourcePaths.get(0) instanceof UriResourceEntitySet)) { + throw new ODataApplicationException("Invalid resource type.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } - } - @Override - public void readEntity(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, - final ContentType requestedContentType) throws ODataApplicationException, SerializerException { - validateOptions(uriInfo.asUriInfoResource()); - - final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); - final Entity entity = readEntityInternal(uriInfo.asUriInfoResource(), edmEntitySet); - if (entity == null) { - throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); - } else { - final ODataFormat format = ODataFormat.fromContentType(requestedContentType); - ODataSerializer serializer = odata.createSerializer(format); - final ExpandOption expand = uriInfo.getExpandOption(); - final SelectOption select = uriInfo.getSelectOption(); - response.setContent(serializer.entity(edmEntitySet, entity, - ODataSerializerOptions.with() - .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : - getContextUrl(serializer, edmEntitySet, true, expand, select, null, null)) - .count(uriInfo.getCountOption()) - .expand(expand).select(select) - .build())); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + final UriResourceEntitySet uriResource = (UriResourceEntitySet) resourcePaths.get(0); + if (uriResource.getTypeFilterOnCollection() != null || uriResource.getTypeFilterOnEntry() != null) { + throw new ODataApplicationException("Type filters are not supported.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } + return uriResource.getEntitySet(); } - @Override - public void countEntitySet(final ODataRequest request, ODataResponse response, final UriInfo uriInfo) - throws ODataApplicationException, SerializerException { - final List resourceParts = uriInfo.asUriInfoResource().getUriResourceParts(); - final int pos = resourceParts.size() - 2; - final EntitySet entitySet = - readEntitySetInternal(((UriResourceEntitySet) resourceParts.get(pos)).getEntitySet(), true); - if (entitySet == null) { - throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); - } else { - response.setContent(new ByteArrayInputStream(entitySet.getCount().toString().getBytes())); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, HttpContentType.TEXT_PLAIN); - } - } - - private EntitySet readEntitySetInternal(final EdmEntitySet edmEntitySet, - final boolean withCount) throws DataProvider.DataProviderException { - EntitySet entitySet = dataProvider.readAll(edmEntitySet); - // TODO: set count (correctly) and next link - if (withCount && entitySet.getCount() == null) { - entitySet.setCount(entitySet.getEntities().size()); - } - return entitySet; - } - - private Entity readEntityInternal(final UriInfoResource uriInfo, final EdmEntitySet entitySet) - throws DataProvider.DataProviderException { - final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0); - return dataProvider.read(entitySet, resourceEntitySet.getKeyPredicates()); - } - - private void validateOptions(final UriInfoResource uriInfo) throws ODataApplicationException { + protected void validateOptions(final UriInfoResource uriInfo) throws ODataApplicationException { if (uriInfo.getCountOption() != null || !uriInfo.getCustomQueryOptions().isEmpty() || uriInfo.getFilterOption() != null @@ -179,151 +79,4 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } } - - private EdmEntitySet getEdmEntitySet(final UriInfoResource uriInfo) throws ODataApplicationException { - final List resourcePaths = uriInfo.getUriResourceParts(); - // first must be entity set - if (!(resourcePaths.get(0) instanceof UriResourceEntitySet)) { - throw new ODataApplicationException("Invalid resource type.", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); - } - List subResPaths = resourcePaths.subList(1, resourcePaths.size()); - for (UriResource subResPath : subResPaths) { - UriResourceKind kind = subResPath.getKind(); - if(kind != UriResourceKind.primitiveProperty - && kind != UriResourceKind.complexProperty - && kind != UriResourceKind.count - && kind != UriResourceKind.value) { - throw new ODataApplicationException("Invalid resource type.", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); - } - } - - final UriResourceEntitySet uriResource = (UriResourceEntitySet) resourcePaths.get(0); - if (uriResource.getTypeFilterOnCollection() != null || uriResource.getTypeFilterOnEntry() != null) { - throw new ODataApplicationException("Type filters are not supported.", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); - } - return uriResource.getEntitySet(); - } - - private ContextURL getContextUrl(final ODataSerializer serializer, - final EdmEntitySet entitySet, final boolean isSingleEntity, - final ExpandOption expand, final SelectOption select, - final List keys, final String propertyPath) throws SerializerException { - return ContextURL.with().entitySet(entitySet) - .selectList(serializer.buildContextURLSelectList(entitySet, expand, select)) - .suffix(isSingleEntity && propertyPath == null ? Suffix.ENTITY : null) - .keyPath(serializer.buildContextURLKeyPredicate(keys)) - .navOrPropertyPath(propertyPath) - .build(); - } - - @Override - public void readProperty(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, - final ContentType contentType) throws ODataApplicationException, SerializerException { - validateOptions(uriInfo.asUriInfoResource()); - - final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); - final List resourceParts = uriInfo.getUriResourceParts(); - final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) resourceParts.get(0); - final Entity entity = readEntityInternal(uriInfo.asUriInfoResource(), edmEntitySet); - - if (entity == null) { - throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); - } else { - final List path = getPropertyPath(resourceParts); - EdmProperty edmProperty = edmEntitySet.getEntityType().getStructuralProperty(path.get(0)); - Property property = entity.getProperty(path.get(0)); - for (final String name : path.subList(1, path.size())) { - if (property != null && (property.isLinkedComplex() || property.isComplex())) { - edmProperty = ((EdmComplexType) edmProperty.getType()).getStructuralProperty(name); - final List complex = property.isLinkedComplex() ? - property.asLinkedComplex().getValue() : - property.asComplex(); - property = null; - for (final Property innerProperty : complex) { - if (innerProperty.getName().equals(name)) { - property = innerProperty; - break; - } - } - } - } - if (property == null) { - throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); - } else { - if (property.getValue() == null) { - response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); - } else { - final ODataFormat format = ODataFormat.fromContentType(contentType); - ODataSerializer serializer = odata.createSerializer(format); - response.setContent(serializer.entityProperty(edmProperty, property, - ODataSerializerOptions.with().contextURL(format == ODataFormat.JSON_NO_METADATA ? null : - getContextUrl(serializer, edmEntitySet, true, null, null, - resourceEntitySet.getKeyPredicates(), buildPropertyPath(path))) - .build())); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, contentType.toContentTypeString()); - } - } - } - } - - private List getPropertyPath(final List path) { - List result = new LinkedList(); - int index = path.size(); - while (path.get(--index) instanceof UriResourceProperty) { - result.add(0, ((UriResourceProperty) path.get(index)).getProperty().getName()); - } - return result; - } - - private String buildPropertyPath(final List path) { - StringBuilder result = new StringBuilder(); - for (final String segment : path) { - result.append(result.length() == 0 ? "" : '/').append(segment); - } - return result.toString(); - } - - @Override - public void readPropertyValue(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, - final ContentType contentType) throws ODataApplicationException, SerializerException { - validateOptions(uriInfo.asUriInfoResource()); - - final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); - final Entity entity = readEntityInternal(uriInfo.asUriInfoResource(), edmEntitySet); - if (entity == null) { - throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); - } else { - final UriResourceProperty uriProperty = - (UriResourceProperty) uriInfo.getUriResourceParts().get(uriInfo.getUriResourceParts().size() - 2); - final EdmProperty edmProperty = uriProperty.getProperty(); - final Property property = entity.getProperty(edmProperty.getName()); - if (property == null || property.getValue() == null) { - response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); - } else { - final EdmPrimitiveType type = (EdmPrimitiveType) edmProperty.getType(); - if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary)) { - response.setContent(new ByteArrayInputStream((byte[]) property.getValue())); - } else { - try { - final String value = type.valueToString(property.getValue(), - edmProperty.isNullable(), edmProperty.getMaxLength(), - edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode()); - response.setContent(new ByteArrayInputStream(value.getBytes("UTF-8"))); - } catch (final EdmPrimitiveTypeException e) { - throw new ODataApplicationException("Error in value formatting.", - HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e); - } catch (final UnsupportedEncodingException e) { - throw new ODataApplicationException("Encoding exception.", - HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e); - } - } - response.setHeader(HttpHeader.CONTENT_TYPE, contentType.toContentTypeString()); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - } - } - } } diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerExceptionHandlingTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerExceptionHandlingTest.java deleted file mode 100644 index b7623b7c9..000000000 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerExceptionHandlingTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; - -import java.util.Collections; -import java.util.Locale; - -import org.apache.olingo.commons.api.ODataException; -import org.apache.olingo.commons.api.edm.FullQualifiedName; -import org.apache.olingo.commons.api.format.ContentType; -import org.apache.olingo.commons.api.http.HttpMethod; -import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.server.api.OData; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.api.ODataRequest; -import org.apache.olingo.server.api.ODataResponse; -import org.apache.olingo.server.api.ServiceMetadata; -import org.apache.olingo.server.api.edm.provider.EdmProvider; -import org.apache.olingo.server.api.edm.provider.EntitySet; -import org.apache.olingo.server.api.edmx.EdmxReference; -import org.apache.olingo.server.api.processor.MetadataProcessor; -import org.apache.olingo.server.api.uri.UriInfo; -import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; -import org.junit.Before; -import org.junit.Test; - -public class ODataHandlerExceptionHandlingTest { - private ODataHandler handler; - - @Before - public void before() { - OData odata = OData.newInstance(); - ServiceMetadata metadata = odata.createServiceMetadata( - new EdmTechProvider(), Collections.emptyList()); - - handler = new ODataHandler(odata, metadata); - } - - @Test - public void wrongHttpMethodForMetadataDocument() throws Exception { - ODataRequest request = new ODataRequest(); - request.setMethod(HttpMethod.POST); - request.setRawODataPath("$metadata"); - ODataResponse response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.PUT); - request.setRawODataPath("$metadata"); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.PATCH); - request.setRawODataPath("$metadata"); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.MERGE); - request.setRawODataPath("$metadata"); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.DELETE); - request.setRawODataPath("$metadata"); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - } - - @Test - public void wrongHttpMethodForServiceDocument() throws Exception { - ODataRequest request = new ODataRequest(); - request.setMethod(HttpMethod.POST); - request.setRawODataPath(""); - ODataResponse response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.PUT); - request.setRawODataPath(""); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.PATCH); - request.setRawODataPath(""); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.MERGE); - request.setRawODataPath(""); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - - request = new ODataRequest(); - request.setMethod(HttpMethod.DELETE); - request.setRawODataPath(""); - response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); - assertNotNull(response.getContent()); - } - - @Test - public void testUriParserExceptionResultsInRightResponseNotFound() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawODataPath("NotFound"); - - ODataResponse response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); - } - - @Test - public void testUriParserExceptionResultsInRightResponseBadRequest() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim('122')"); - - ODataResponse response = handler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); - } - - @Test - public void testUriParserExceptionResultsInRightResponseEdmCause() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawODataPath("EdmException"); - - OData odata = OData.newInstance(); - ServiceMetadata serviceMetadata = odata.createServiceMetadata(new EdmProvider() { - public EntitySet getEntitySet(final FullQualifiedName entityContainer, final String entitySetName) - throws ODataException { - throw new ODataException("msg"); - } - }, Collections.emptyList()); - - ODataHandler localHandler = new ODataHandler(odata, serviceMetadata); - - ODataResponse response = localHandler.process(request); - assertNotNull(response); - assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); - } - - @Test - public void testWithApplicationExceptionInProcessor() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawODataPath("$metadata"); - - MetadataProcessor metadataProcessor = mock(MetadataProcessor.class); - doThrow(new ODataApplicationException("msg", 425, Locale.ENGLISH)).when(metadataProcessor).readMetadata( - any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); - - handler.register(metadataProcessor); - - ODataResponse response = handler.process(request); - assertNotNull(response); - assertEquals(425, response.getStatusCode()); - } -} diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java index 176918dd2..f49b4d47e 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java @@ -18,15 +18,21 @@ */ package org.apache.olingo.server.core; +import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; -import java.util.Arrays; import java.util.Collections; +import java.util.Locale; import org.apache.commons.io.IOUtils; +import org.apache.olingo.commons.api.ODataException; +import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpContentType; @@ -34,311 +40,305 @@ import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpMethod; import org.apache.olingo.commons.api.http.HttpStatusCode; import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.ODataApplicationException; import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.edm.provider.EdmProvider; +import org.apache.olingo.server.api.edm.provider.EntitySet; import org.apache.olingo.server.api.edmx.EdmxReference; -import org.apache.olingo.server.api.processor.EntitySetProcessor; +import org.apache.olingo.server.api.processor.ComplexTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.ComplexTypeProcessor; +import org.apache.olingo.server.api.processor.CountEntityTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityTypeProcessor; import org.apache.olingo.server.api.processor.MetadataProcessor; -import org.apache.olingo.server.api.processor.PropertyProcessor; +import org.apache.olingo.server.api.processor.PrimitiveTypeCollectionProcessor; +import org.apache.olingo.server.api.processor.PrimitiveTypeProcessor; +import org.apache.olingo.server.api.processor.Processor; import org.apache.olingo.server.api.processor.ServiceDocumentProcessor; import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; -import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; public class ODataHandlerTest { - private ODataHandler handler; - - @Before - public void before() { - OData odata = OData.newInstance(); - ServiceMetadata metadata = odata.createServiceMetadata( - new EdmTechProvider(), Collections.emptyList()); - - handler = new ODataHandler(odata, metadata); - } + private static final String BASE_URI = "http://localhost/odata"; @Test - public void testServiceDocumentNonDefault() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawBaseUri("http://localhost/odata"); - request.setRawODataPath("/"); - - ServiceDocumentProcessor processor = mock(ServiceDocumentProcessor.class); - handler.register(processor); - - ODataResponse response = handler.process(request); - - assertNotNull(response); + public void serviceDocumentNonDefault() throws Exception { + final ServiceDocumentProcessor processor = mock(ServiceDocumentProcessor.class); + final ODataResponse response = dispatch(HttpMethod.GET, "/", processor); assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + + verify(processor).readServiceDocument( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, "/", processor); + dispatchMethodNotAllowed(HttpMethod.PUT, "/", processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, "/", processor); + dispatchMethodNotAllowed(HttpMethod.MERGE, "/", processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, "/", processor); } @Test - public void testServiceDocumentDefault() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawBaseUri("http://localhost/odata"); - request.setRawODataPath("/"); - - ODataResponse response = handler.process(request); - - assertNotNull(response); + public void serviceDocumentDefault() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "/", null); assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); String ct = response.getHeaders().get(HttpHeader.CONTENT_TYPE); - assertTrue(ct.contains("application/json")); - assertTrue(ct.contains("odata.metadata=minimal")); + assertThat(ct, containsString("application/json")); + assertThat(ct, containsString("odata.metadata=minimal")); assertNotNull(response.getContent()); String doc = IOUtils.toString(response.getContent()); - assertTrue(doc.contains("\"@odata.context\" : \"http://localhost/odata/$metadata\"")); - assertTrue(doc.contains("\"value\" :")); + assertThat(doc, containsString("\"@odata.context\" : \"" + BASE_URI + "/$metadata\"")); + assertThat(doc, containsString("\"value\" :")); } @Test - public void testServiceDocumentRedirect() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawBaseUri("http://localhost/odata"); - request.setRawRequestUri("http://localhost/odata"); - request.setRawODataPath(""); - - ODataResponse response = handler.process(request); - - assertNotNull(response); + public void serviceDocumentRedirect() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "", null); assertEquals(HttpStatusCode.TEMPORARY_REDIRECT.getStatusCode(), response.getStatusCode()); - assertEquals("http://localhost/odata/", response.getHeaders().get(HttpHeader.LOCATION)); + assertEquals(BASE_URI + "/", response.getHeaders().get(HttpHeader.LOCATION)); } @Test - public void testMetadataNonDefault() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawODataPath("$metadata"); - - MetadataProcessor processor = mock(MetadataProcessor.class); - handler.register(processor); - - ODataResponse response = handler.process(request); - - assertNotNull(response); + public void metadataNonDefault() throws Exception { + final MetadataProcessor processor = mock(MetadataProcessor.class); + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", processor); assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + + verify(processor).readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.PUT, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.MERGE, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, "$metadata", processor); } @Test - public void testMetadataDefault() throws Exception { - ODataRequest request = new ODataRequest(); - - request.setMethod(HttpMethod.GET); - request.setRawODataPath("$metadata"); - - ODataResponse response = handler.process(request); - - assertNotNull(response); + public void metadataDefault() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null); assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); assertEquals(HttpContentType.APPLICATION_XML, response.getHeaders().get(HttpHeader.CONTENT_TYPE)); assertNotNull(response.getContent()); - String doc = IOUtils.toString(response.getContent()); - - assertTrue(doc.contains(" emptyList()); + ODataRequest request = new ODataRequest(); - request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim/$count"); + request.setRawODataPath("EdmException"); - EntitySetProcessor processor = mock(EntitySetProcessor.class); - handler.register(processor); - - ODataResponse response = handler.process(request); + final ODataResponse response = new ODataHandler(odata, serviceMetadata).process(request); assertNotNull(response); - Mockito.verify(processor).countEntitySet( - Mockito.eq(request), - Mockito.any(ODataResponse.class), - Mockito.any(UriInfo.class)); + assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + } + + @Test + public void dispatchEntitySet() throws Exception { + final EntityTypeCollectionProcessor processor = mock(EntityTypeCollectionProcessor.class); + dispatch(HttpMethod.GET, "ESAllPrim", processor); + + verify(processor).readEntityTypeCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + } + + @Test + public void dispatchEntitySetCount() throws Exception { + final CountEntityTypeCollectionProcessor processor = mock(CountEntityTypeCollectionProcessor.class); + dispatch(HttpMethod.GET, "ESAllPrim/$count", processor); + + verify(processor).countEntityTypeCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, "ESAllPrim/$count", processor); } @Test public void dispatchCountWithNavigation() throws Exception { - ODataRequest request = new ODataRequest(); + final CountEntityTypeCollectionProcessor processor = mock(CountEntityTypeCollectionProcessor.class); + dispatch(HttpMethod.GET, "ESAllPrim(0)/NavPropertyETTwoPrimMany/$count", processor); - request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim(0)/NavPropertyETTwoPrimMany/$count"); - - EntitySetProcessor processor = mock(EntitySetProcessor.class); - handler.register(processor); - - ODataResponse response = handler.process(request); - assertNotNull(response); - Mockito.verify(processor).countEntitySet( - Mockito.eq(request), - Mockito.any(ODataResponse.class), - Mockito.any(UriInfo.class)); + verify(processor).countEntityTypeCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); } @Test - public void dispatchAddressPrimitiveProperty() throws Exception { - ODataRequest request = new ODataRequest(); + public void dispatchEntity() throws Exception { + final EntityTypeProcessor processor = mock(EntityTypeProcessor.class); + dispatch(HttpMethod.GET, "ESAllPrim(0)", processor); - request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim(0)/PropertyInt16"); - - PropertyProcessor processor = mock(PropertyProcessor.class); - handler.register(processor); - - ODataResponse response = handler.process(request); - assertNotNull(response); - - Mockito.verify(processor).readProperty( - Mockito.any(ODataRequest.class), - Mockito.any(ODataResponse.class), - Mockito.any(UriInfo.class), - Mockito.any(ContentType.class)); + verify(processor).readEntityType( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); } @Test - public void dispatchAddressPrimitivePropertyValue() throws Exception { - ODataRequest request = new ODataRequest(); + public void dispatchPrimitiveProperty() throws Exception { + final PrimitiveTypeProcessor processor = mock(PrimitiveTypeProcessor.class); + dispatch(HttpMethod.GET, "ESAllPrim(0)/PropertyInt16", processor); - request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESAllPrim(0)/PropertyInt16/$value"); - - PropertyProcessor processor = mock(PropertyProcessor.class); - handler.register(processor); - - ODataResponse response = handler.process(request); - assertNotNull(response); - - Mockito.verify(processor).readPropertyValue( - Mockito.any(ODataRequest.class), - Mockito.any(ODataResponse.class), - Mockito.any(UriInfo.class), - Mockito.any(ContentType.class)); + verify(processor).readPrimitiveType( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); } @Test - public void dispatchAddressComplexProperty() throws Exception { - ODataRequest request = new ODataRequest(); + public void dispatchPrimitivePropertyValue() throws Exception { + final PrimitiveTypeProcessor processor = mock(PrimitiveTypeProcessor.class); + dispatch(HttpMethod.GET, "ESAllPrim(0)/PropertyInt16/$value", processor); - request.setMethod(HttpMethod.GET); - request.setRawODataPath("ESMixPrimCollComp(7)/PropertyComp"); - - PropertyProcessor processor = mock(PropertyProcessor.class); - handler.register(processor); - - ODataResponse response = handler.process(request); - assertNotNull(response); - - Mockito.verify(processor).readProperty( - Mockito.any(ODataRequest.class), - Mockito.any(ODataResponse.class), - Mockito.any(UriInfo.class), - Mockito.any(ContentType.class)); + verify(processor).readPrimitiveTypeAsValue( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); } -} \ No newline at end of file + + @Test + public void dispatchPrimitiveCollectionProperty() throws Exception { + final PrimitiveTypeCollectionProcessor processor = mock(PrimitiveTypeCollectionProcessor.class); + dispatch(HttpMethod.GET, "ESMixPrimCollComp(7)/CollPropertyString", processor); + + verify(processor).readPrimitiveTypeCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + } + + @Test + public void dispatchComplexProperty() throws Exception { + final ComplexTypeProcessor processor = mock(ComplexTypeProcessor.class); + dispatch(HttpMethod.GET, "ESMixPrimCollComp(7)/PropertyComp", processor); + + verify(processor).readComplexType( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + } + + @Test + public void dispatchComplexCollectionProperty() throws Exception { + final ComplexTypeCollectionProcessor processor = mock(ComplexTypeCollectionProcessor.class); + dispatch(HttpMethod.GET, "ESMixPrimCollComp(7)/CollPropertyComp", processor); + + verify(processor).readComplexTypeCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + } + + private ODataResponse dispatch(final HttpMethod method, final String path, final String query, + final String headerName, final String headerValue, final Processor processor) { + ODataRequest request = new ODataRequest(); + request.setMethod(method); + request.setRawBaseUri(BASE_URI); + if (path.isEmpty()) { + request.setRawRequestUri(BASE_URI); + } + request.setRawODataPath(path); + request.setRawQueryPath(query); + + if (headerName != null) { + request.addHeader(headerName, Collections.singletonList(headerValue)); + } + + final OData odata = OData.newInstance(); + final ServiceMetadata metadata = odata.createServiceMetadata( + new EdmTechProvider(), Collections. emptyList()); + + ODataHandler handler = new ODataHandler(odata, metadata); + + if (processor != null) { + handler.register(processor); + } + + final ODataResponse response = handler.process(request); + assertNotNull(response); + return response; + } + + private ODataResponse dispatch(final HttpMethod method, final String path, final Processor processor) { + return dispatch(method, path, null, null, null, processor); + } + + private void dispatchMethodNotAllowed(final HttpMethod method, final String path, final Processor processor) { + final ODataResponse response = dispatch(method, path, processor); + assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); + assertNotNull(response.getContent()); + } +}