Repository: olingo-odata4
Updated Branches:
  refs/heads/master ecf9b9f9b -> 6a736db10


[OLINGO-888] Support Singletons in OData dispatcher

Contributed by Archana Rai via
https://issues.apache.org/jira/browse/OLINGO-888


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/84a052dc
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/84a052dc
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/84a052dc

Branch: refs/heads/master
Commit: 84a052dcfa3825c37377c4cfd93c01f2c7e6af53
Parents: ecf9b9f
Author: Christian Amend <[email protected]>
Authored: Tue Nov 22 14:13:14 2016 +0100
Committer: Christian Amend <[email protected]>
Committed: Tue Nov 22 14:13:14 2016 +0100

----------------------------------------------------------------------
 .../olingo/server/core/ODataDispatcher.java     | 215 ++++++++++++-------
 .../olingo/server/tecsvc/data/DataCreator.java  |  38 +++-
 .../olingo/server/tecsvc/data/DataProvider.java |   9 +
 .../processor/TechnicalEntityProcessor.java     |  39 +++-
 .../tecsvc/processor/TechnicalProcessor.java    |  53 +++--
 .../server/core/ODataHandlerImplTest.java       | 163 +++++++++++++-
 6 files changed, 420 insertions(+), 97 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java
index 138f45a..5d48e34 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java
@@ -25,6 +25,7 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.edm.EdmReturnType;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpMethod;
@@ -70,6 +71,7 @@ import 
org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty;
 import org.apache.olingo.server.api.uri.UriResourceProperty;
 import org.apache.olingo.server.core.batchhandler.BatchHandler;
 import org.apache.olingo.server.core.etag.PreconditionsValidator;
+import org.apache.olingo.server.api.uri.UriResourceSingleton;
 
 public class ODataDispatcher {
 
@@ -144,7 +146,11 @@ public class ODataDispatcher {
       handleEntityDispatching(request, response,
           ((UriResourcePartTyped) lastPathSegment).isCollection(), 
isEntityOrNavigationMedia(lastPathSegment));
       break;
-
+      
+    case singleton:
+      handleSingleEntityDispatching(request, response, 
isSingletonMedia(lastPathSegment), true);
+      break;
+      
     case count:
       checkMethod(request.getMethod(), HttpMethod.GET);
       handleCountDispatching(request, response, lastPathSegmentIndex);
@@ -304,62 +310,80 @@ public class ODataDispatcher {
   private void handleValueDispatching(final ODataRequest request, final 
ODataResponse response,
       final int lastPathSegmentIndex) throws ODataApplicationException, 
ODataLibraryException {
     // The URI Parser already checked if $value is allowed here so we only 
have to dispatch to the correct processor
-    final HttpMethod method = request.getMethod();
     final UriResource resource = 
uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1);
     if (resource instanceof UriResourceProperty
         || resource instanceof UriResourceFunction
             && ((UriResourceFunction) resource).getType().getKind() == 
EdmTypeKind.PRIMITIVE) {
-      final EdmType type = resource instanceof UriResourceProperty ? 
((UriResourceProperty) resource).getType()
-          : ((UriResourceFunction) resource).getType();
-      final RepresentationType valueRepresentationType =
-          type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? 
RepresentationType.BINARY
-              : RepresentationType.VALUE;
-      if (method == HttpMethod.GET) {
-        final ContentType requestedContentType = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
-            request, handler.getCustomContentTypeSupport(), 
valueRepresentationType);
+      handlePrimitiveValueDispatching(request, response, resource);
+    } else {
+      handleMediaValueDispatching(request, response, resource);
+    }
+  }
 
-        handler.selectProcessor(PrimitiveValueProcessor.class)
-            .readPrimitiveValue(request, response, uriInfo, 
requestedContentType);
-      } else if (method == HttpMethod.PUT && resource instanceof 
UriResourceProperty) {
-        validatePreconditions(request, false);
-        final ContentType requestFormat = 
getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
-            valueRepresentationType, true);
-        final ContentType responseFormat = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
-            request, handler.getCustomContentTypeSupport(), 
valueRepresentationType);
-        handler.selectProcessor(PrimitiveValueProcessor.class)
-            .updatePrimitiveValue(request, response, uriInfo, requestFormat, 
responseFormat);
-      } else if (method == HttpMethod.DELETE && resource instanceof 
UriResourceProperty) {
-        validatePreconditions(request, false);
-        handler.selectProcessor(PrimitiveValueProcessor.class)
-            .deletePrimitiveValue(request, response, uriInfo);
-      } else {
-        throwMethodNotAllowed(method);
-      }
+  private void handleMediaValueDispatching(final ODataRequest request, final 
ODataResponse response,
+      final UriResource resource) throws ContentNegotiatorException, 
+     ODataApplicationException, ODataLibraryException,
+      ODataHandlerException, PreconditionException {
+    final HttpMethod method = request.getMethod();
+    if (method == HttpMethod.GET) {
+      // This can be a GET on an EntitySet, Navigation or Function
+      final ContentType requestedContentType = ContentNegotiator.
+          doContentNegotiation(uriInfo.getFormatOption(),
+          request, handler.getCustomContentTypeSupport(), 
RepresentationType.MEDIA);
+      handler.selectProcessor(MediaEntityProcessor.class)
+          .readMediaEntity(request, response, uriInfo, requestedContentType);
+      // PUT and DELETE can only be called on EntitySets or Navigation 
properties which are media resources
+    } else if (method == HttpMethod.PUT && 
(isEntityOrNavigationMedia(resource) 
+        || isSingletonMedia(resource))) {
+      validatePreconditions(request, true);
+      final ContentType requestFormat = 
ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE));
+      final ContentType responseFormat = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+          request, handler.getCustomContentTypeSupport(), 
RepresentationType.ENTITY);
+      handler.selectProcessor(MediaEntityProcessor.class)
+          .updateMediaEntity(request, response, uriInfo, requestFormat, 
responseFormat);
+    } else if (method == HttpMethod.DELETE && 
isEntityOrNavigationMedia(resource)) {
+      validatePreconditions(request, true);
+      handler.selectProcessor(MediaEntityProcessor.class)
+          .deleteMediaEntity(request, response, uriInfo);
     } else {
-      if (method == HttpMethod.GET) {
-        // This can be a GET on an EntitySet, Navigation or Function
-        final ContentType requestedContentType = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
-            request, handler.getCustomContentTypeSupport(), 
RepresentationType.MEDIA);
-        handler.selectProcessor(MediaEntityProcessor.class)
-            .readMediaEntity(request, response, uriInfo, requestedContentType);
-        // PUT and DELETE can only be called on EntitySets or Navigation 
properties which are media resources
-      } else if (method == HttpMethod.PUT && 
isEntityOrNavigationMedia(resource)) {
-        validatePreconditions(request, true);
-        final ContentType requestFormat = 
ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE));
-        final ContentType responseFormat = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
-            request, handler.getCustomContentTypeSupport(), 
RepresentationType.ENTITY);
-        handler.selectProcessor(MediaEntityProcessor.class)
-            .updateMediaEntity(request, response, uriInfo, requestFormat, 
responseFormat);
-      } else if (method == HttpMethod.DELETE && 
isEntityOrNavigationMedia(resource)) {
-        validatePreconditions(request, true);
-        handler.selectProcessor(MediaEntityProcessor.class)
-            .deleteMediaEntity(request, response, uriInfo);
-      } else {
-        throwMethodNotAllowed(method);
-      }
+      throwMethodNotAllowed(method);
     }
   }
+  
+  private void handlePrimitiveValueDispatching(final ODataRequest request, 
final ODataResponse response,
+      final UriResource resource) throws ContentNegotiatorException, 
+  ODataApplicationException, ODataLibraryException,
+      ODataHandlerException, PreconditionException {
+    final HttpMethod method = request.getMethod();
+    final EdmType type = resource instanceof UriResourceProperty ?
+        ((UriResourceProperty) resource).getType() : ((UriResourceFunction) 
resource).getType();
+    final RepresentationType valueRepresentationType =
+        type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ?
+            RepresentationType.BINARY : RepresentationType.VALUE;
+    if (method == HttpMethod.GET) {
+      final ContentType requestedContentType = ContentNegotiator.
+          doContentNegotiation(uriInfo.getFormatOption(),
+          request, handler.getCustomContentTypeSupport(), 
valueRepresentationType);
 
+      handler.selectProcessor(PrimitiveValueProcessor.class)
+          .readPrimitiveValue(request, response, uriInfo, 
requestedContentType);
+    } else if (method == HttpMethod.PUT && resource instanceof 
UriResourceProperty) {
+      validatePreconditions(request, false);
+      final ContentType requestFormat = 
getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
+          valueRepresentationType, true);
+      final ContentType responseFormat = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+          request, handler.getCustomContentTypeSupport(), 
valueRepresentationType);
+      handler.selectProcessor(PrimitiveValueProcessor.class)
+          .updatePrimitiveValue(request, response, uriInfo, requestFormat, 
responseFormat);
+    } else if (method == HttpMethod.DELETE && resource instanceof 
UriResourceProperty) {
+      validatePreconditions(request, false);
+      handler.selectProcessor(PrimitiveValueProcessor.class)
+          .deletePrimitiveValue(request, response, uriInfo);
+    } else {
+      throwMethodNotAllowed(method);
+    }
+  }
+  
   private void handleComplexDispatching(final ODataRequest request, final 
ODataResponse response,
       final boolean isCollection) throws ODataApplicationException, 
ODataLibraryException {
     final HttpMethod method = request.getMethod();
@@ -466,44 +490,76 @@ public class ODataDispatcher {
 
   private void handleEntityDispatching(final ODataRequest request, final 
ODataResponse response,
       final boolean isCollection, final boolean isMedia) throws 
ODataApplicationException, ODataLibraryException {
-    final HttpMethod method = request.getMethod();
     if (isCollection) {
-      if (method == HttpMethod.GET) {
-        final ContentType requestedContentType = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
-            request, handler.getCustomContentTypeSupport(), 
RepresentationType.COLLECTION_ENTITY);
-        handler.selectProcessor(EntityCollectionProcessor.class)
-            .readEntityCollection(request, response, uriInfo, 
requestedContentType);
-      } else if (method == HttpMethod.POST) {
-        final ContentType responseFormat = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
-            request, handler.getCustomContentTypeSupport(), 
RepresentationType.ENTITY);
-        if (isMedia) {
-          final ContentType requestFormat = 
ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE));
-          handler.selectProcessor(MediaEntityProcessor.class)
-              .createMediaEntity(request, response, uriInfo, requestFormat, 
responseFormat);
-        } else {
-          final ContentType requestFormat = 
getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
-              RepresentationType.ENTITY, true);
-          handler.selectProcessor(EntityProcessor.class)
-              .createEntity(request, response, uriInfo, requestFormat, 
responseFormat);
-        }
+      handleEntityCollectionDispatching(request, response, isMedia);
       } else {
-        throwMethodNotAllowed(method);
+        handleSingleEntityDispatching(request, response, isMedia, false);
+      }
+  }
+
+  
+  private void handleEntityCollectionDispatching(final ODataRequest request, 
final ODataResponse response,
+      final boolean isMedia
+      ) throws ContentNegotiatorException, ODataApplicationException, 
ODataLibraryException,
+          ODataHandlerException {
+    final HttpMethod method = request.getMethod();
+    if (method == HttpMethod.GET) {
+      final ContentType requestedContentType = ContentNegotiator.
+          doContentNegotiation(uriInfo.getFormatOption(),
+          request, handler.getCustomContentTypeSupport(), 
RepresentationType.COLLECTION_ENTITY);
+      handler.selectProcessor(EntityCollectionProcessor.class)
+          .readEntityCollection(request, response, uriInfo, 
requestedContentType);
+    } else if (method == HttpMethod.POST) {
+      final ContentType responseFormat = ContentNegotiator.
+          doContentNegotiation(uriInfo.getFormatOption(),
+          request, handler.getCustomContentTypeSupport(), 
RepresentationType.ENTITY);
+      if (isMedia) {
+        final ContentType requestFormat = ContentType.parse(
+            request.getHeader(HttpHeader.CONTENT_TYPE));
+        handler.selectProcessor(MediaEntityProcessor.class)
+            .createMediaEntity(request, response, uriInfo, requestFormat, 
responseFormat);
+      } else {
+        final ContentType requestFormat = getSupportedContentType(
+            request.getHeader(HttpHeader.CONTENT_TYPE),
+            RepresentationType.ENTITY, true);
+        handler.selectProcessor(EntityProcessor.class)
+            .createEntity(request, response, uriInfo, requestFormat, 
responseFormat);
       }
     } else {
+      throwMethodNotAllowed(method);
+    }
+  }
+  
+  private boolean isSingletonMedia(final UriResource pathSegment) { 
+   return pathSegment instanceof UriResourceSingleton
+       && ((UriResourceSingleton) pathSegment).getEntityType().hasStream();
+  }
+
+  
+   
+  private void handleSingleEntityDispatching(final ODataRequest request, final 
ODataResponse response,
+        final boolean isMedia, final boolean isSingleton) throws 
+    ContentNegotiatorException, ODataApplicationException,
+        ODataLibraryException, ODataHandlerException, PreconditionException {
+      final HttpMethod method = request.getMethod();
       if (method == HttpMethod.GET) {
-        final ContentType requestedContentType = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+        final ContentType requestedContentType = ContentNegotiator.
+            doContentNegotiation(uriInfo.getFormatOption(),
             request, handler.getCustomContentTypeSupport(), 
RepresentationType.ENTITY);
         handler.selectProcessor(EntityProcessor.class)
             .readEntity(request, response, uriInfo, requestedContentType);
       } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) {
         validatePreconditions(request, false);
-        final ContentType requestFormat = 
getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
+        final ContentType requestFormat = getSupportedContentType(
+            request.getHeader(HttpHeader.CONTENT_TYPE),
             RepresentationType.ENTITY, true);
-        final ContentType responseFormat = 
ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+        final ContentType responseFormat = ContentNegotiator.
+            doContentNegotiation(uriInfo.getFormatOption(),
             request, handler.getCustomContentTypeSupport(), 
RepresentationType.ENTITY);
         handler.selectProcessor(EntityProcessor.class)
             .updateEntity(request, response, uriInfo, requestFormat, 
responseFormat);
-      } else if (method == HttpMethod.DELETE) {
+      } else if (method == HttpMethod.DELETE && !isSingleton) {
+        validateIsSingleton(method);
         validatePreconditions(request, false);
         handler.selectProcessor(isMedia ? MediaEntityProcessor.class : 
EntityProcessor.class)
             .deleteEntity(request, response, uriInfo);
@@ -511,8 +567,23 @@ public class ODataDispatcher {
         throwMethodNotAllowed(method);
       }
     }
+
+  /*Delete method is not allowed for Entities navigating to Singleton*/ 
+  private void validateIsSingleton(HttpMethod method) throws 
ODataHandlerException {
+   final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1;
+   final UriResource pathSegment = 
uriInfo.getUriResourceParts().get(lastPathSegmentIndex);
+   if(pathSegment instanceof UriResourceNavigation){
+   if(uriInfo.getUriResourceParts().get(lastPathSegmentIndex-1) instanceof 
UriResourceEntitySet){
+     if(((UriResourceEntitySet)uriInfo.getUriResourceParts().
+         get(lastPathSegmentIndex-1)).getEntitySet().getRelatedBindingTarget(
+         pathSegment.getSegmentValue())instanceof EdmSingleton){
+         throwMethodNotAllowed(method);
+       }
+     }
+   }
   }
 
+
   private void validatePreconditions(final ODataRequest request, final boolean 
isMediaValue)
       throws PreconditionException {
     // If needed perform preconditions validation.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java
index dfc1d78..39f7c81 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java
@@ -88,13 +88,27 @@ public class DataCreator {
     data.put("ESStream", createESStream(edm, odata));
     data.put("ESWithStream", createESWithStream(edm, odata));
     data.put("ESPeople", createESPeople(edm, odata));
+    data.put("SINav", createSINav(edm, odata));
+    data.put("SI", createESTwoPrim(edm, odata));
 
+    linkSINav(data);
     linkESTwoPrim(data);
     linkESAllPrim(data);
     linkESKeyNav(data);
     linkESTwoKeyNav(data);
     linkESPeople(data);
   }
+  
+  private EntityCollection createSINav(Edm edm, OData odata) {
+    final EntityCollection entityCollection = new EntityCollection();
+
+    entityCollection.getEntities().add(createESTwoKeyNavEntity((short) 1, 
"1"));
+
+  setEntityType(entityCollection, 
edm.getEntityType(EntityTypeProvider.nameETTwoKeyNav));
+  createEntityId(edm, odata, "ESTwoKeyNav", entityCollection);
+  createOperations("ESTwoKeyNav", entityCollection, 
EntityTypeProvider.nameETTwoKeyNav);
+  return entityCollection;
+}
 
   private EntityCollection createESMixEnumDefCollComp(Edm edm, OData odata) {
     final EntityCollection entityCollection = new EntityCollection();
@@ -1362,7 +1376,8 @@ public class DataCreator {
     final EntityCollection entityCollection = data.get("ESTwoKeyNav");
     final List<Entity> esKeyNavTargets = data.get("ESKeyNav").getEntities();
     final List<Entity> esTwoKeyNavTargets = 
data.get("ESTwoKeyNav").getEntities();
-
+    final  List<Entity> SINav = data.get("SINav").getEntities();
+    
     // NavPropertyETKeyNavOne
     setLink(entityCollection.getEntities().get(0), "NavPropertyETKeyNavOne", 
esKeyNavTargets.get(0));
     setLink(entityCollection.getEntities().get(1), "NavPropertyETKeyNavOne", 
esKeyNavTargets.get(0));
@@ -1387,6 +1402,27 @@ public class DataCreator {
         esTwoKeyNavTargets.get(1));
     setLinks(entityCollection.getEntities().get(1), 
"NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(0));
     setLinks(entityCollection.getEntities().get(2), 
"NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(1));
+    // NavPropertySINav
+    setLink(entityCollection.getEntities().get(0), "NavPropertySINav", 
SINav.get(0));
+       setLink(entityCollection.getEntities().get(1), "NavPropertySINav", 
SINav.get(0));
+       setLink(entityCollection.getEntities().get(2), "NavPropertySINav", 
SINav.get(0));
+
+  }
+
+   private void linkSINav(final Map<String, EntityCollection> data) {
+     final EntityCollection entityCollection = data.get("SINav");
+     final List<Entity> esKeyNavTargets = data.get("ESKeyNav").getEntities();
+     final List<Entity> esTwoKeyNavTargets = 
data.get("ESTwoKeyNav").getEntities();
+
+     // NavPropertyETKeyNavOne
+     setLink(entityCollection.getEntities().get(0), "NavPropertyETKeyNavOne", 
esKeyNavTargets.get(0));
+   
+     // NavPropertyETTwoKeyNavOne
+     setLink(entityCollection.getEntities().get(0), 
"NavPropertyETTwoKeyNavOne", esTwoKeyNavTargets.get(0));
+
+     // NavPropertyETTwoKeyNavMany
+     setLinks(entityCollection.getEntities().get(0), 
"NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(0),
+         esTwoKeyNavTargets.get(1));
   }
 
   protected static Property createPrimitive(final String name, final Object 
value) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
index 465ab80..b526887 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
@@ -51,6 +51,7 @@ 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.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
 import org.apache.olingo.commons.api.edm.FullQualifiedName;
@@ -700,4 +701,12 @@ public class DataProvider {
       super(message, statusCode.getStatusCode(), Locale.ROOT, throwable);
     }
   }
+  
+  public Entity read(EdmSingleton singleton) {
+    if (data.containsKey(singleton.getName())) {
+    EntityCollection entitySet = data.get(singleton.getName());
+    return entitySet.getEntities().get(0);
+    }
+    return null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
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
index e6b625e..da2e936 100644
--- 
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
@@ -64,6 +64,7 @@ import org.apache.olingo.server.api.uri.UriInfo;
 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.UriResourceSingleton;
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.IdOption;
@@ -421,10 +422,11 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     //
 
     final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo);
-    final EdmEntityType edmEntityType = edmEntitySet == null ?
-        (EdmEntityType) ((UriResourcePartTyped) uriInfo.getUriResourceParts()
-            .get(uriInfo.getUriResourceParts().size() - 1)).getType() :
-        edmEntitySet.getEntityType();
+    
+    //for Singleton/$ref edmEntityset will be null throw error
+    validateSingletonRef(isReference,edmEntitySet);
+   
+    final EdmEntityType edmEntityType = getEdmType(uriInfo, edmEntitySet);
 
     final Entity entity = readEntity(uriInfo);
 
@@ -456,6 +458,35 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     response.setHeader(HttpHeader.CONTENT_TYPE, 
requestedFormat.toContentTypeString());
   }
 
+  /*This method validates if the $ref is called directly on Singleton
+   * Error is thrown when $ref is called on a Singleton as it is not 
implemented*/
+  private void validateSingletonRef(boolean isReference, EdmEntitySet 
edmEntitySet) throws
+  ODataApplicationException {
+   if(isReference && edmEntitySet==null){
+         throw new ODataApplicationException("$ref not implemented on 
singleton",
+                    HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), 
Locale.ROOT);
+        
+      }
+  
+  }
+  
+  /*This method returns edmType of the entityset or Singleton*/  
+  private EdmEntityType getEdmType(UriInfo uriInfo, EdmEntitySet edmEntitySet) 
{
+    if(edmEntitySet!=null){
+      return edmEntitySet.getEntityType();
+    }else if(edmEntitySet==null && uriInfo.getUriResourceParts()
+              .get(uriInfo.getUriResourceParts().size() - 1) instanceof 
UriResourcePartTyped){
+      return  (EdmEntityType) ((UriResourcePartTyped) 
uriInfo.getUriResourceParts()
+                .get(uriInfo.getUriResourceParts().size() - 1)).getType();
+    }else if((UriResourceSingleton) uriInfo.getUriResourceParts()
+              .get(0) instanceof UriResourceSingleton){
+      return (EdmEntityType)((UriResourceSingleton) 
uriInfo.getUriResourceParts()
+                .get(0)).getType();
+    }
+
+  return null;
+  }
+
   private void readEntityCollection(final ODataRequest request, final 
ODataResponse response,
       final UriInfo uriInfo, final ContentType requestedContentType, final 
boolean isReference)
       throws ODataApplicationException, ODataLibraryException {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
----------------------------------------------------------------------
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 c59bec9..47a9a63 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
@@ -29,6 +29,7 @@ import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
 import org.apache.olingo.commons.api.edm.EdmFunction;
 import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.server.api.OData;
@@ -43,6 +44,7 @@ import org.apache.olingo.server.api.uri.UriResourceAction;
 import org.apache.olingo.server.api.uri.UriResourceEntitySet;
 import org.apache.olingo.server.api.uri.UriResourceFunction;
 import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.api.uri.UriResourceSingleton;
 import org.apache.olingo.server.tecsvc.data.DataProvider;
 
 /**
@@ -72,7 +74,8 @@ public abstract class TechnicalProcessor implements Processor 
{
   protected EdmEntitySet getEdmEntitySet(final UriInfoResource uriInfo) throws 
ODataApplicationException {
     EdmEntitySet entitySet = null;
     final List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
-
+    EdmSingleton singleton = null;
+    
     // First must be an entity, an entity collection, a function import, or an 
action import.
     blockTypeFilters(resourcePaths.get(0));
     if (resourcePaths.get(0) instanceof UriResourceEntitySet) {
@@ -81,30 +84,41 @@ public abstract class TechnicalProcessor implements 
Processor {
       entitySet = ((UriResourceFunction) 
resourcePaths.get(0)).getFunctionImport().getReturnedEntitySet();
     } else if (resourcePaths.get(0) instanceof UriResourceAction) {
       entitySet = ((UriResourceAction) 
resourcePaths.get(0)).getActionImport().getReturnedEntitySet();
+    }else if (resourcePaths.get(0) instanceof UriResourceSingleton ) {      
+      singleton =((UriResourceSingleton) resourcePaths.get(0)).getSingleton();
     } else {
       throw new ODataApplicationException("Invalid resource type.",
           HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
     }
 
+    entitySet = (EdmEntitySet) getEntitySetForNavigation(entitySet, singleton, 
resourcePaths);
+
+    return entitySet;
+  }
+  
+  private EdmBindingTarget getEntitySetForNavigation(EdmEntitySet entitySet, 
EdmSingleton singleton,
+      List<UriResource> resourcePaths) throws ODataApplicationException {
     int navigationCount = 0;
-    while (entitySet != null
-        && ++navigationCount < resourcePaths.size()
-        && resourcePaths.get(navigationCount) instanceof 
UriResourceNavigation) {
-      final UriResourceNavigation uriResourceNavigation = 
(UriResourceNavigation) resourcePaths.get(navigationCount);
-      blockTypeFilters(uriResourceNavigation);
-      if (uriResourceNavigation.getProperty().containsTarget()) {
-        throw new ODataApplicationException("Containment navigation is not 
supported.",
-            HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
-      }
-      final EdmBindingTarget target = 
entitySet.getRelatedBindingTarget(uriResourceNavigation.getProperty().getName());
-      if (target instanceof EdmEntitySet) {
-        entitySet = (EdmEntitySet) target;
-      } else {
-        throw new ODataApplicationException("Singletons are not supported.",
-            HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+      while ((entitySet != null || singleton!=null)
+          && ++navigationCount < resourcePaths.size()
+          && resourcePaths.get(navigationCount) instanceof 
UriResourceNavigation) {
+        final UriResourceNavigation uriResourceNavigation = 
+            (UriResourceNavigation) resourcePaths.get(navigationCount);
+        blockTypeFilters(uriResourceNavigation);
+        if (uriResourceNavigation.getProperty().containsTarget()) {
+          throw new ODataApplicationException("Containment navigation is not 
supported.",
+              HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+        }
+        EdmBindingTarget target = null ;
+        if(entitySet!=null){
+          target = 
entitySet.getRelatedBindingTarget(uriResourceNavigation.getProperty().getName());
+        }else if(singleton != null){
+          target = 
singleton.getRelatedBindingTarget(uriResourceNavigation.getProperty().getName());
+        }
+        if (target instanceof EdmEntitySet) {
+          entitySet = (EdmEntitySet) target;
+        }
       }
-    }
-
     return entitySet;
   }
 
@@ -130,6 +144,9 @@ public abstract class TechnicalProcessor implements 
Processor {
     if (resourcePaths.get(0) instanceof UriResourceEntitySet) {
       final UriResourceEntitySet uriResource = (UriResourceEntitySet) 
resourcePaths.get(0);
       entity = dataProvider.read(uriResource.getEntitySet(), 
uriResource.getKeyPredicates());
+    }else if (resourcePaths.get(0) instanceof UriResourceSingleton) {
+      final UriResourceSingleton uriResource = (UriResourceSingleton) 
resourcePaths.get(0);
+      entity = dataProvider.read( uriResource.getSingleton());
     } else if (resourcePaths.get(0) instanceof UriResourceFunction) {
       final UriResourceFunction uriResource = (UriResourceFunction) 
resourcePaths.get(0);
       final EdmFunction function = uriResource.getFunction();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/84a052dc/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java
 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java
index 591e8e2..bacb700 100644
--- 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java
+++ 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java
@@ -624,6 +624,112 @@ public class ODataHandlerImplTest {
     dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor);
   }
 
+
+  @Test
+  public void dispatchSingleton() throws Exception {
+    final String uri = "SI";
+    final EntityProcessor processor = mock(EntityProcessor.class);
+    
+    dispatch(HttpMethod.GET, uri, processor);
+    verify(processor).readEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class));
+
+    dispatch(HttpMethod.PATCH, uri, processor);
+    verify(processor).updateEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.PUT, uri, processor);
+    verify(processor, times(2)).updateEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+
+    dispatchMethodNotAllowed(HttpMethod.POST, uri, processor);
+    dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor);
+  }
+  
+  @Test
+  public void dispatchSingletonMedia() throws Exception {
+    final String uri = "SIMedia/$value";
+    final MediaEntityProcessor processor = mock(MediaEntityProcessor.class);
+    
+    dispatch(HttpMethod.GET, uri, processor);
+    verify(processor).readMediaEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class));
+
+    dispatch(HttpMethod.PUT, uri, processor);
+    verify(processor).updateMediaEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+
+    dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor);
+    dispatchMethodNotAllowed(HttpMethod.POST, uri, processor);
+    dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor);
+  }
+  
+  @Test
+  public void dispatchSingletonNavigation() throws Exception {
+    final String uri = "SINav/NavPropertyETTwoKeyNavOne";
+    final String sigletonNavUri = 
"ESTwoKeyNav(PropertyInt16=1,PropertyString='1')/NavPropertySINav";
+    final String sigletonManyNavUri = "SINav/NavPropertyETTwoKeyNavMany";
+    final EntityProcessor processor = mock(EntityProcessor.class);
+    final EntityCollectionProcessor collectionProcessor = 
mock(EntityCollectionProcessor.class);
+    
+    dispatch(HttpMethod.GET, sigletonNavUri, processor);
+    verify(processor).readEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class));
+
+    dispatch(HttpMethod.PATCH, sigletonNavUri, processor);
+    verify(processor).updateEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.PUT, sigletonNavUri, processor);
+    verify(processor, times(2)).updateEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+
+    dispatchMethodNotAllowed(HttpMethod.POST, sigletonNavUri, processor);
+    dispatchMethodNotAllowed(HttpMethod.DELETE, sigletonNavUri, processor);
+    
+    dispatch(HttpMethod.GET, uri, processor);
+    verify(processor, times(2)).readEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class));
+
+    dispatch(HttpMethod.PATCH, uri, processor);
+    verify(processor,  times(3)).updateEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.PUT, uri, processor);
+    verify(processor, times(4)).updateEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+    
+    dispatchMethodNotAllowed(HttpMethod.POST, uri, processor);
+
+    dispatch(HttpMethod.DELETE, uri, processor);
+    verify(processor).deleteEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class));
+    
+    
+    dispatch(HttpMethod.GET, sigletonManyNavUri, collectionProcessor);
+    verify(collectionProcessor).readEntityCollection(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class));
+    
+    dispatchMethodNotAllowed(HttpMethod.PATCH, sigletonManyNavUri, processor);
+    
+    dispatchMethodNotAllowed(HttpMethod.PUT, sigletonManyNavUri, processor);
+    
+    dispatch(HttpMethod.POST, sigletonManyNavUri, processor);
+    verify(processor).createEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), 
any(ContentType.class),
+        any(ContentType.class));
+
+
+    dispatchMethodNotAllowed(HttpMethod.DELETE, sigletonManyNavUri, processor);
+  }
+  
   @Test
   public void dispatchMedia() throws Exception {
     final String uri = "ESMedia(1)/$value";
@@ -859,6 +965,9 @@ public class ODataHandlerImplTest {
   public void dispatchReference() throws Exception {
     final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimOne/$ref";
     final String uriMany = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref";
+    final String singletonUri = "SINav/NavPropertyETKeyNavOne/$ref";
+    final String singletonUriMany = "SINav/NavPropertyETTwoKeyNavMany/$ref";
+    final String singleUri = "SINav/$ref";
     final ReferenceProcessor processor = mock(ReferenceProcessor.class);
 
     dispatch(HttpMethod.GET, uri, processor);
@@ -882,12 +991,52 @@ public class ODataHandlerImplTest {
     dispatch(HttpMethod.DELETE, uriMany, "$id=ESTwoPrim(1)", null, null, 
processor);
     verify(processor).deleteReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class));
     
-    dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor);
+    dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor);    
+    
+    //singleton URIs
+    
+    dispatch(HttpMethod.GET, singletonUri, processor);
+    verify(processor, times(2)).readReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.PATCH, singletonUri, processor);
+    verify(processor, times(3)).updateReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.PUT, singletonUri, processor);
+    verify(processor, times(4)).updateReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatchMethodNotAllowed(HttpMethod.POST, singletonUri, processor); 
+    
+    dispatch(HttpMethod.GET, singleUri, processor);
+    verify(processor, times(3)).readReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.PATCH, singleUri, processor);
+    verify(processor, times(5)).updateReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.PUT, singleUri, processor);
+    verify(processor, times(6)).updateReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatchMethodNotAllowed(HttpMethod.POST, singleUri, processor); 
+    
+    dispatch(HttpMethod.POST, singletonUriMany, processor);
+    verify(processor, times(2)).createReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatch(HttpMethod.DELETE, singletonUriMany, "$id=ESTwoPrim(1)", null, 
null, processor);
+    verify(processor, times(2)).deleteReference(any(ODataRequest.class), 
any(ODataResponse.class), any(UriInfo.class));
+    
+    dispatchMethodNotAllowed(HttpMethod.HEAD, singletonUriMany, processor);
   }
 
   @Test
   public void dispatchReferenceCollection() throws Exception {
     final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref";
+    final String singletonUri = "SINav/NavPropertyETTwoKeyNavMany/$ref";
     final ReferenceCollectionProcessor processor = 
mock(ReferenceCollectionProcessor.class);
 
     dispatch(HttpMethod.GET, uri, processor);
@@ -896,7 +1045,17 @@ public class ODataHandlerImplTest {
 
     dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor);
     dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor);
-    dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor);
+    dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); 
+    
+    //singleton ref
+    dispatch(HttpMethod.GET, singletonUri, processor);
+    verify(processor, 
times(2)).readReferenceCollection(any(ODataRequest.class), 
+        any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class));
+
+    dispatchMethodNotAllowed(HttpMethod.PATCH, singletonUri, processor);
+    dispatchMethodNotAllowed(HttpMethod.PUT, singletonUri, processor);
+    dispatchMethodNotAllowed(HttpMethod.HEAD, singletonUri, processor);
   }
 
   @Test

Reply via email to