Repository: olingo-odata4
Updated Branches:
  refs/heads/master f2fd03553 -> 120adfea5


OLINGO-1009: adding cycle detection when =max is used during serialization


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

Branch: refs/heads/master
Commit: 120adfea500adb4bbf50370a6c4353df23938a28
Parents: f2fd035
Author: Ramesh Reddy <[email protected]>
Authored: Thu Sep 8 21:07:18 2016 -0500
Committer: Ramesh Reddy <[email protected]>
Committed: Fri Sep 9 14:23:26 2016 -0500

----------------------------------------------------------------------
 .../ExpandWithSystemQueryOptionsITCase.java     |  43 +-
 .../serializer/json/ODataJsonSerializer.java    | 143 ++++---
 .../core/serializer/xml/ODataXmlSerializer.java | 218 ++++++----
 .../olingo/server/tecsvc/data/DataCreator.java  |  80 +++-
 .../tecsvc/provider/ContainerProvider.java      |   8 +-
 .../tecsvc/provider/EntityTypeProvider.java     |  11 +-
 .../tecsvc/provider/PropertyProvider.java       |  16 +
 .../xml/ODataXmlDeserializerTest.java           |   1 -
 .../json/ODataJsonSerializerTest.java           | 158 ++++++-
 .../serializer/xml/ODataXmlSerializerTest.java  | 408 ++++++++++++++++++-
 10 files changed, 873 insertions(+), 213 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
index 5f44351..e1dd2f5 100644
--- 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
+++ 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
@@ -419,26 +419,9 @@ public class ExpandWithSystemQueryOptionsITCase extends 
AbstractParamTecSvcITCas
         .getEntities()
         .get(0);
 
-    assertShortOrInt(1, 
entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
-    assertEquals("1", 
entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
-
-    
assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
-    assertEquals(2, 
entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
-        .asInlineEntitySet()
-        .getEntitySet()
-        .getEntities()
-        .size());
-
-    final List<ClientEntity> fourthLevelEntites = 
entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
-        .asInlineEntitySet()
-        .getEntitySet()
-        .getEntities();
-
-    assertShortOrInt(1, 
fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
-    assertEquals("1", 
fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
-
-    assertShortOrInt(1, 
fourthLevelEntites.get(1).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
-    assertEquals("2", 
fourthLevelEntites.get(1).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+    // cycle happens here
+    assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')", 
entityThirdLevel.getId().toASCIIString());
+    assertEquals(0, entityThirdLevel.getProperties().size());
   }
 
   @Test
@@ -495,23 +478,9 @@ public class ExpandWithSystemQueryOptionsITCase extends 
AbstractParamTecSvcITCas
         .getEntities()
         .get(0);
 
-    assertShortOrInt(1, 
entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
-    assertEquals("1", 
entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
-
-    
assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
-    assertEquals(1, 
entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
-        .asInlineEntitySet()
-        .getEntitySet()
-        .getEntities()
-        .size());
-
-    final List<ClientEntity> fourthLevelEntites = 
entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
-        .asInlineEntitySet()
-        .getEntitySet()
-        .getEntities();
-
-    assertShortOrInt(1, 
fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
-    assertEquals("1", 
fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+    // cycle happens here
+    assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')", 
entityThirdLevel.getId().toASCIIString());
+    assertEquals(0, entityThirdLevel.getProperties().size());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
index 513df38..f6e6015 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
@@ -166,10 +166,10 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
       writeOperations(entitySet.getOperations(), json);
       json.writeFieldName(Constants.VALUE);
       if (options == null) {
-        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
false, json);
+        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
false, null, json);
       } else {
         writeEntitySet(metadata, entityType, entitySet,
-            options.getExpand(), null, options.getSelect(), 
options.getWriteOnlyReferences(), json);
+            options.getExpand(), null, options.getSelect(), 
options.getWriteOnlyReferences(), null, json);
       }
       writeNextLink(entitySet, json);
 
@@ -212,10 +212,10 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
       }
       json.writeFieldName(Constants.VALUE);
       if (options == null) {
-        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
false, json);
+        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
false, null, json);
       } else {
         writeEntitySet(metadata, entityType, entitySet,
-            options.getExpand(), null, options.getSelect(), 
options.getWriteOnlyReferences(), json);
+            options.getExpand(), null, options.getSelect(), 
options.getWriteOnlyReferences(), null, json);
       }
       // next link not supported by default for streaming results
 //      writeNextLink(entitySet, json);
@@ -243,6 +243,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
           null,
           options == null ? null : options.getSelect(),
           options == null ? false : options.getWriteOnlyReferences(),
+          null,
           json);
 
       json.close();
@@ -268,7 +269,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
 
   protected void writeEntitySet(final ServiceMetadata metadata, final 
EdmEntityType entityType,
       final AbstractEntityCollection entitySet, final ExpandOption expand, 
Integer toDepth, final SelectOption select,
-      final boolean onlyReference, final JsonGenerator json) throws 
IOException,
+      final boolean onlyReference, final Set<String> ancestors, final 
JsonGenerator json) throws IOException,
       SerializerException {
     json.writeStartArray();
     for (final Entity entity : entitySet) {
@@ -277,7 +278,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
         json.writeStringField(Constants.JSON_ID, getEntityId(entity));
         json.writeEndObject();
       } else {
-        writeEntity(metadata, entityType, entity, null, expand, toDepth, 
select, false, json);
+        writeEntity(metadata, entityType, entity, null, expand, toDepth, 
select, false, ancestors, json);
       }
     }
     json.writeEndArray();
@@ -310,59 +311,81 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
     return true;
   }
 
-  public void writeEntity(final ServiceMetadata metadata, final EdmEntityType 
entityType, final Entity entity,
+  private boolean cycleDetected(final Set<String> ancestors, String child) {
+      if (ancestors == null) {
+          return false;
+      }
+      return ancestors.contains(child);
+  }
+  
+  protected void writeEntity(final ServiceMetadata metadata, final 
EdmEntityType entityType, final Entity entity,
       final ContextURL contextURL, final ExpandOption expand, Integer toDepth, 
-      final SelectOption select, final boolean onlyReference, final 
JsonGenerator json)
+      final SelectOption select, final boolean onlyReference, Set<String> 
ancestors, final JsonGenerator json)
       throws IOException, SerializerException {
-    json.writeStartObject();
-    if (!isODataMetadataNone) {
-      // top-level entity
-      if (contextURL != null) {
-        writeContextURL(contextURL, json);
-        writeMetadataETag(metadata, json);
-      }
-      if (entity.getETag() != null) {
-        json.writeStringField(Constants.JSON_ETAG, entity.getETag());
+    boolean cycle = false;
+    if (expand != null && cycleDetected(ancestors, getEntityId(entity))) {
+        cycle = true;
+    } else {
+      if (ancestors == null) {
+        ancestors = new HashSet<String>();
       }
-      if (entityType.hasStream()) {
-        if (entity.getMediaETag() != null) {
-          json.writeStringField(Constants.JSON_MEDIA_ETAG, 
entity.getMediaETag());
-        }
-        if (entity.getMediaContentType() != null) {
-          json.writeStringField(Constants.JSON_MEDIA_CONTENT_TYPE, 
entity.getMediaContentType());
+      ancestors.add(getEntityId(entity));
+    }
+    try {
+      json.writeStartObject();
+      if (!isODataMetadataNone) {
+        // top-level entity
+        if (contextURL != null) {
+          writeContextURL(contextURL, json);
+          writeMetadataETag(metadata, json);
         }
-        if (entity.getMediaContentSource() != null) {
-          json.writeStringField(Constants.JSON_MEDIA_READ_LINK, 
entity.getMediaContentSource().toString());
+        if (entity.getETag() != null) {
+          json.writeStringField(Constants.JSON_ETAG, entity.getETag());
         }
-        if (entity.getMediaEditLinks() != null && 
!entity.getMediaEditLinks().isEmpty()) {
-          json.writeStringField(Constants.JSON_MEDIA_EDIT_LINK, 
entity.getMediaEditLinks().get(0).getHref());
+        if (entityType.hasStream()) {
+          if (entity.getMediaETag() != null) {
+            json.writeStringField(Constants.JSON_MEDIA_ETAG, 
entity.getMediaETag());
+          }
+          if (entity.getMediaContentType() != null) {
+            json.writeStringField(Constants.JSON_MEDIA_CONTENT_TYPE, 
entity.getMediaContentType());
+          }
+          if (entity.getMediaContentSource() != null) {
+            json.writeStringField(Constants.JSON_MEDIA_READ_LINK, 
entity.getMediaContentSource().toString());
+          }
+          if (entity.getMediaEditLinks() != null && 
!entity.getMediaEditLinks().isEmpty()) {
+            json.writeStringField(Constants.JSON_MEDIA_EDIT_LINK, 
entity.getMediaEditLinks().get(0).getHref());
+          }
         }
       }
-    }
-    if (onlyReference) {
-      json.writeStringField(Constants.JSON_ID, getEntityId(entity));
-    } else {
-      final EdmEntityType resolvedType = resolveEntityType(metadata, 
entityType, entity.getType());
-      if ((!isODataMetadataNone && !resolvedType.equals(entityType)) || 
isODataMetadataFull) {
-        json.writeStringField(Constants.JSON_TYPE, "#" + entity.getType());
-      }
-      if ((!isODataMetadataNone && !areKeyPredicateNamesSelected(select, 
resolvedType)) || isODataMetadataFull) {
+      if (cycle || onlyReference) {
         json.writeStringField(Constants.JSON_ID, getEntityId(entity));
-      }
-      
-      if (isODataMetadataFull) {
-        if (entity.getSelfLink() != null) {
-          json.writeStringField(Constants.JSON_READ_LINK, 
entity.getSelfLink().getHref());
+      } else {
+        final EdmEntityType resolvedType = resolveEntityType(metadata, 
entityType, entity.getType());
+        if ((!isODataMetadataNone && !resolvedType.equals(entityType)) || 
isODataMetadataFull) {
+          json.writeStringField(Constants.JSON_TYPE, "#" + entity.getType());
+        }
+        if ((!isODataMetadataNone && !areKeyPredicateNamesSelected(select, 
resolvedType)) || isODataMetadataFull) {
+          json.writeStringField(Constants.JSON_ID, getEntityId(entity));
         }
-        if (entity.getEditLink() != null) {
-          json.writeStringField(Constants.JSON_EDIT_LINK, 
entity.getEditLink().getHref());
+        
+        if (isODataMetadataFull) {
+          if (entity.getSelfLink() != null) {
+            json.writeStringField(Constants.JSON_READ_LINK, 
entity.getSelfLink().getHref());
+          }
+          if (entity.getEditLink() != null) {
+            json.writeStringField(Constants.JSON_EDIT_LINK, 
entity.getEditLink().getHref());
+          }
         }
+        
+        writeProperties(metadata, resolvedType, entity.getProperties(), 
select, json);
+        writeNavigationProperties(metadata, resolvedType, entity, expand, 
toDepth, ancestors, json);
+        writeOperations(entity.getOperations(), json);      
       }
-      
-      writeProperties(metadata, resolvedType, entity.getProperties(), select, 
json);
-      writeNavigationProperties(metadata, resolvedType, entity, expand, 
toDepth, json);
-      writeOperations(entity.getOperations(), json);
       json.writeEndObject();
+    } finally {
+      if (!cycle && ancestors != null) {
+        ancestors.remove(getEntityId(entity));
+      }
     }
   }
 
@@ -446,7 +469,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
 
   protected void writeNavigationProperties(final ServiceMetadata metadata,
       final EdmStructuredType type, final Linked linked, final ExpandOption 
expand, final Integer toDepth,
-      final JsonGenerator json) throws SerializerException, IOException {
+      final Set<String> ancestors, final JsonGenerator json) throws 
SerializerException, IOException {
     if ((toDepth != null && toDepth > 1) || (toDepth == null && 
ExpandSelectHelper.hasExpand(expand))) {
       final ExpandItem expandAll = ExpandSelectHelper.getExpandAll(expand);
       for (final String propertyName : type.getNavigationPropertyNames()) {
@@ -460,6 +483,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
                 innerOptions == null ? null : innerOptions.getCountOption(),
                 innerOptions == null ? false : innerOptions.hasCountPath(),
                 innerOptions == null ? false : innerOptions.isRef(),
+                ancestors,
                 json);
           continue;
         }
@@ -493,14 +517,15 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
               levels = levelsOption.getValue();
             }
           }
-          
+                             
           writeExpandedNavigationProperty(metadata, property, navigationLink,
-              childExpand, levels,
-              innerOptions == null ? null : innerOptions.getSelectOption(),
-              innerOptions == null ? null : innerOptions.getCountOption(),
-              innerOptions == null ? false : innerOptions.hasCountPath(),
-              innerOptions == null ? false : innerOptions.isRef(),
-              json);
+            childExpand, levels,
+            innerOptions == null ? null : innerOptions.getSelectOption(),
+            innerOptions == null ? null : innerOptions.getCountOption(),
+            innerOptions == null ? false : innerOptions.hasCountPath(),
+            innerOptions == null ? false : innerOptions.isRef(),
+            ancestors,
+            json);
         }
       }
     } else if (isODataMetadataFull) {
@@ -521,7 +546,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
       final ServiceMetadata metadata, final EdmNavigationProperty property,
       final Link navigationLink, final ExpandOption innerExpand,
       Integer toDepth, final SelectOption innerSelect, final CountOption 
innerCount,
-      final boolean writeOnlyCount, final boolean writeOnlyRef,
+      final boolean writeOnlyCount, final boolean writeOnlyRef, final 
Set<String> ancestors,
       final JsonGenerator json) throws IOException, SerializerException {
 
     if (property.isCollection()) {
@@ -545,7 +570,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
           }
           json.writeFieldName(property.getName());
           writeEntitySet(metadata, property.getType(), 
navigationLink.getInlineEntitySet(), innerExpand, toDepth,
-              innerSelect, writeOnlyRef, json);
+              innerSelect, writeOnlyRef, ancestors, json);
         }
       }
     } else {
@@ -554,7 +579,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
         json.writeNull();
       } else {
         writeEntity(metadata, property.getType(), 
navigationLink.getInlineEntity(), null,
-            innerExpand, toDepth, innerSelect, writeOnlyRef, json);
+            innerExpand, toDepth, innerSelect, writeOnlyRef, ancestors, json);
       }
     }
   }
@@ -883,7 +908,7 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
       writeProperties(metadata, type, values, options == null ? null : 
options.getSelect(), json);
       if (!property.isNull() && property.isComplex()) {
         writeNavigationProperties(metadata, type, property.asComplex(),
-            options == null ? null : options.getExpand(), null, json);
+            options == null ? null : options.getExpand(), null, null, json);
       }
       json.writeEndObject();
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java
index d6de50f..56ee391 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java
@@ -259,11 +259,11 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
 
       boolean writeOnlyRef = (options != null && 
options.getWriteOnlyReferences());
       if (options == null) {
-        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
null, writer, writeOnlyRef);
+        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
null, writer, writeOnlyRef, null);
       } else {
         writeEntitySet(metadata, entityType, entitySet,
             options.getExpand(), null, 
-            options.getSelect(), options.xml10InvalidCharReplacement(), 
writer, writeOnlyRef);
+            options.getSelect(), options.xml10InvalidCharReplacement(), 
writer, writeOnlyRef, null);
       }
 
       writer.writeEndElement();
@@ -317,11 +317,11 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
 
       boolean writeOnlyRef = (options != null && 
options.getWriteOnlyReferences());
       if (options == null) {
-        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
null, writer, writeOnlyRef);
+        writeEntitySet(metadata, entityType, entitySet, null, null, null, 
null, writer, writeOnlyRef,null);
       } else {
         writeEntitySet(metadata, entityType, entitySet,
             options.getExpand(), null, 
-            options.getSelect(), options.xml10InvalidCharReplacement(), 
writer, writeOnlyRef);
+            options.getSelect(), options.xml10InvalidCharReplacement(), 
writer, writeOnlyRef,null);
       }
 
       writer.writeEndElement();
@@ -363,7 +363,7 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
           null,
           options == null ? null : options.getSelect(),
           options == null ? null : options.xml10InvalidCharReplacement(),
-          writer, true, false);
+          writer, true, false, null);
       writer.writeEndDocument();
 
       writer.flush();
@@ -404,99 +404,135 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
   protected void writeEntitySet(final ServiceMetadata metadata, final 
EdmEntityType entityType,
       final AbstractEntityCollection entitySet, final ExpandOption expand, 
       final Integer toDepth, final SelectOption select,
-      final String xml10InvalidCharReplacement,final XMLStreamWriter writer, 
final boolean writeOnlyRef) 
+      final String xml10InvalidCharReplacement,final XMLStreamWriter writer, 
+      final boolean writeOnlyRef,final Set<String> ancestors) 
           throws XMLStreamException, SerializerException {
     for (final Entity entity : entitySet) {
       writeEntity(metadata, entityType, entity, null, expand, toDepth, select, 
-          xml10InvalidCharReplacement, writer, false, writeOnlyRef);
+          xml10InvalidCharReplacement, writer, false, writeOnlyRef, ancestors);
     }
   }
+  
+  /**
+   * Get the ascii representation of the entity id
+   * or thrown an {@link SerializerException} if id is <code>null</code>.
+   *
+   * @param entity the entity
+   * @return ascii representation of the entity id
+   */
+  private String getEntityId(Entity entity) throws SerializerException {
+    if(entity.getId() == null) {
+      throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+    }
+    return entity.getId().toASCIIString();
+  }  
 
+  private boolean cycleDetected(final Set<String> ancestors, String child) {
+      if (ancestors == null) {
+          return false;
+      }
+      return ancestors.contains(child);
+  }
+  
   protected void writeEntity(final ServiceMetadata metadata, final 
EdmEntityType entityType,
       final Entity entity, final ContextURL contextURL, final ExpandOption 
expand, final Integer toDepth,
       final SelectOption select, final String xml10InvalidCharReplacement,
-      final XMLStreamWriter writer, final boolean top, final boolean 
writeOnlyRef)
+      final XMLStreamWriter writer, final boolean top, final boolean 
writeOnlyRef, Set<String> ancestors)
       throws XMLStreamException, SerializerException {
+    boolean cycle = false;
+    if (expand != null && cycleDetected(ancestors, getEntityId(entity))) {
+      cycle = true;
+    } else {
+      if (ancestors == null) {
+        ancestors = new HashSet<String>();
+      }
+      ancestors.add(getEntityId(entity));
+    }
 
-    if (writeOnlyRef) {
+    if (cycle || writeOnlyRef) {
       writeReference(entity, contextURL, writer, top);
       return;
     }
-    writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ENTRY, NS_ATOM);
-    if (top) {
-      writer.writeNamespace(ATOM, NS_ATOM);
-      writer.writeNamespace(METADATA, NS_METADATA);
-      writer.writeNamespace(DATA, NS_DATA);
-
-      if (contextURL != null) { // top-level entity
-        writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT,
-            ContextURLBuilder.create(contextURL).toASCIIString());
-        writeMetadataETag(metadata, writer);
+    try {
+      writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ENTRY, NS_ATOM);
+      if (top) {
+        writer.writeNamespace(ATOM, NS_ATOM);
+        writer.writeNamespace(METADATA, NS_METADATA);
+        writer.writeNamespace(DATA, NS_DATA);
+  
+        if (contextURL != null) { // top-level entity
+          writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT,
+              ContextURLBuilder.create(contextURL).toASCIIString());
+          writeMetadataETag(metadata, writer);
+        }
       }
-    }
-    if (entity.getETag() != null) {
-      writer.writeAttribute(METADATA, NS_METADATA, Constants.ATOM_ATTR_ETAG, 
entity.getETag());
-    }
-
-    if (entity.getId() != null) {
-      writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_ID);
-      writer.writeCharacters(entity.getId().toASCIIString());
-      writer.writeEndElement();
-    }
-
-    writerAuthorInfo(entity.getTitle(), writer);
-
-    if (entity.getId() != null) {
-      writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_LINK);
-      writer.writeAttribute(Constants.ATTR_REL, Constants.EDIT_LINK_REL);
-      writer.writeAttribute(Constants.ATTR_HREF, 
entity.getId().toASCIIString());
-      writer.writeEndElement();
-    }
-
-    if (entityType.hasStream()) {
-      writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT);
-      writer.writeAttribute(Constants.ATTR_TYPE, entity.getMediaContentType());
-      if (entity.getMediaContentSource() != null) {
-        writer.writeAttribute(Constants.ATOM_ATTR_SRC, 
entity.getMediaContentSource().toString());
-      } else {
-        String id = entity.getId().toASCIIString();
-        writer.writeAttribute(Constants.ATOM_ATTR_SRC,
-            id + (id.endsWith("/") ? "" : "/") + "$value");
+      if (entity.getETag() != null) {
+        writer.writeAttribute(METADATA, NS_METADATA, Constants.ATOM_ATTR_ETAG, 
entity.getETag());
       }
+  
+      if (entity.getId() != null) {
+        writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_ID);
+        writer.writeCharacters(entity.getId().toASCIIString());
+        writer.writeEndElement();
+      }
+  
+      writerAuthorInfo(entity.getTitle(), writer);
+  
+      if (entity.getId() != null) {
+        writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_LINK);
+        writer.writeAttribute(Constants.ATTR_REL, Constants.EDIT_LINK_REL);
+        writer.writeAttribute(Constants.ATTR_HREF, 
entity.getId().toASCIIString());
+        writer.writeEndElement();
+      }
+  
+      if (entityType.hasStream()) {
+        writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT);
+        writer.writeAttribute(Constants.ATTR_TYPE, 
entity.getMediaContentType());
+        if (entity.getMediaContentSource() != null) {
+          writer.writeAttribute(Constants.ATOM_ATTR_SRC, 
entity.getMediaContentSource().toString());
+        } else {
+          String id = entity.getId().toASCIIString();
+          writer.writeAttribute(Constants.ATOM_ATTR_SRC,
+              id + (id.endsWith("/") ? "" : "/") + "$value");
+        }
+        writer.writeEndElement();
+      }
+  
+      // write media links
+      for (Link link : entity.getMediaEditLinks()) {
+        writeLink(writer, link);
+      }
+  
+      EdmEntityType resolvedType = resolveEntityType(metadata, entityType, 
entity.getType());
+      writeNavigationProperties(metadata, resolvedType, entity, expand,
+        toDepth, xml10InvalidCharReplacement, ancestors, writer);
+  
+      writer.writeStartElement(ATOM, Constants.ATOM_ELEM_CATEGORY, NS_ATOM);
+      writer.writeAttribute(Constants.ATOM_ATTR_SCHEME, Constants.NS_SCHEME);
+      writer.writeAttribute(Constants.ATOM_ATTR_TERM,
+          "#" + 
resolvedType.getFullQualifiedName().getFullQualifiedNameAsString());
       writer.writeEndElement();
+  
+      // In the case media, content is sibiling
+      if (!entityType.hasStream()) {
+        writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT);
+        writer.writeAttribute(Constants.ATTR_TYPE, "application/xml");
+      }
+  
+      writer.writeStartElement(METADATA, Constants.PROPERTIES, NS_METADATA);
+      writeProperties(metadata, resolvedType, entity.getProperties(), select, 
xml10InvalidCharReplacement, writer);
+      writer.writeEndElement(); // properties
+  
+      if (!entityType.hasStream()) { // content
+        writer.writeEndElement();
+      }
+      
+      writeOperations(entity.getOperations(), writer);
+      
+      writer.writeEndElement(); // entry
+    } finally {
+      ancestors.remove(getEntityId(entity));
     }
-
-    // write media links
-    for (Link link : entity.getMediaEditLinks()) {
-      writeLink(writer, link);
-    }
-
-    EdmEntityType resolvedType = resolveEntityType(metadata, entityType, 
entity.getType());
-    writeNavigationProperties(metadata, resolvedType, entity, expand, toDepth, 
xml10InvalidCharReplacement, writer);
-
-    writer.writeStartElement(ATOM, Constants.ATOM_ELEM_CATEGORY, NS_ATOM);
-    writer.writeAttribute(Constants.ATOM_ATTR_SCHEME, Constants.NS_SCHEME);
-    writer.writeAttribute(Constants.ATOM_ATTR_TERM,
-        "#" + 
resolvedType.getFullQualifiedName().getFullQualifiedNameAsString());
-    writer.writeEndElement();
-
-    // In the case media, content is sibiling
-    if (!entityType.hasStream()) {
-      writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT);
-      writer.writeAttribute(Constants.ATTR_TYPE, "application/xml");
-    }
-
-    writer.writeStartElement(METADATA, Constants.PROPERTIES, NS_METADATA);
-    writeProperties(metadata, resolvedType, entity.getProperties(), select, 
xml10InvalidCharReplacement, writer);
-    writer.writeEndElement(); // properties
-
-    if (!entityType.hasStream()) { // content
-      writer.writeEndElement();
-    }
-    
-    writeOperations(entity.getOperations(), writer);
-    
-    writer.writeEndElement(); // entry
   }
 
   private void writeOperations(final List<Operation> operations,
@@ -598,7 +634,7 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
 
   protected void writeNavigationProperties(final ServiceMetadata metadata,
       final EdmStructuredType type, final Linked linked, final ExpandOption 
expand, final Integer toDepth,
-      final String xml10InvalidCharReplacement, final XMLStreamWriter writer) 
+      final String xml10InvalidCharReplacement, final Set<String> ancestors, 
final XMLStreamWriter writer) 
           throws SerializerException, XMLStreamException {
     if ((toDepth != null && toDepth > 1) || (toDepth == null && 
ExpandSelectHelper.hasExpand(expand))) {
       final ExpandItem expandAll = ExpandSelectHelper.getExpandAll(expand);
@@ -615,7 +651,7 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
               innerOptions == null ? null : innerOptions.getCountOption(),
               innerOptions == null ? false : innerOptions.hasCountPath(),
               innerOptions == null ? false : innerOptions.isRef(),             
                       
-              xml10InvalidCharReplacement, writer);
+              xml10InvalidCharReplacement, ancestors, writer);
           writer.writeEndElement();
           writer.writeEndElement();
           continue;
@@ -654,12 +690,12 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
           writeLink(writer, navigationLink, false);
           writer.writeStartElement(METADATA, Constants.ATOM_ELEM_INLINE, 
NS_METADATA);
           writeExpandedNavigationProperty(metadata, property, navigationLink,
-              childExpand, levels,
-              innerOptions == null ? null : innerOptions.getSelectOption(),
-              innerOptions == null ? null : innerOptions.getCountOption(),
-              innerOptions == null ? false : innerOptions.hasCountPath(),
-              innerOptions == null ? false : innerOptions.isRef(),             
                       
-              xml10InvalidCharReplacement, writer);
+            childExpand, levels,
+            innerOptions == null ? null : innerOptions.getSelectOption(),
+            innerOptions == null ? null : innerOptions.getCountOption(),
+            innerOptions == null ? false : innerOptions.hasCountPath(),
+            innerOptions == null ? false : innerOptions.isRef(),               
                     
+            xml10InvalidCharReplacement, ancestors, writer);
           writer.writeEndElement();
           writer.writeEndElement();
         } else {
@@ -720,6 +756,7 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
       final ExpandOption innerExpand, final Integer toDepth, 
       final SelectOption innerSelect, final CountOption coutOption, 
       final boolean writeNavigationCount, final boolean writeOnlyRef,final 
String xml10InvalidCharReplacement,
+      final Set<String> ancestors,
       final XMLStreamWriter writer) throws XMLStreamException, 
SerializerException {
     if (property.isCollection()) {
       if (navigationLink != null && navigationLink.getInlineEntitySet() != 
null) {
@@ -731,14 +768,15 @@ public class ODataXmlSerializer extends 
AbstractODataSerializer {
             writeCount(navigationLink.getInlineEntitySet(), writer);
           }
           writeEntitySet(metadata, property.getType(), 
navigationLink.getInlineEntitySet(), innerExpand, toDepth,
-              innerSelect, xml10InvalidCharReplacement, writer, writeOnlyRef);
+              innerSelect, xml10InvalidCharReplacement, writer, writeOnlyRef, 
ancestors);
         }
         writer.writeEndElement();
       }
     } else {
       if (navigationLink != null && navigationLink.getInlineEntity() != null) {
         writeEntity(metadata, property.getType(), 
navigationLink.getInlineEntity(), null,
-            innerExpand, toDepth, innerSelect, xml10InvalidCharReplacement, 
writer, false, writeOnlyRef);
+            innerExpand, toDepth, innerSelect, xml10InvalidCharReplacement, 
writer, 
+            false, writeOnlyRef, ancestors);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/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 60f0273..dfc1d78 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
@@ -87,11 +87,13 @@ public class DataCreator {
     data.put("ESMixEnumDefCollComp", createESMixEnumDefCollComp(edm, odata));
     data.put("ESStream", createESStream(edm, odata));
     data.put("ESWithStream", createESWithStream(edm, odata));
+    data.put("ESPeople", createESPeople(edm, odata));
 
     linkESTwoPrim(data);
     linkESAllPrim(data);
     linkESKeyNav(data);
     linkESTwoKeyNav(data);
+    linkESPeople(data);
   }
 
   private EntityCollection createESMixEnumDefCollComp(Edm edm, OData odata) {
@@ -1101,33 +1103,57 @@ public class DataCreator {
     return entityCollection;
   }
 
-  private EntityCollection createESWithStream(final Edm edm, final OData 
odata) {
-    EntityCollection entityCollection = new EntityCollection();
+  private EntityCollection createESPeople(final Edm edm, final OData odata) {
+      EntityCollection entityCollection = new EntityCollection();
 
-    Link readLink = new Link();
-    readLink.setRel(Constants.NS_MEDIA_READ_LINK_REL);
-    readLink.setHref("readLink");
-    
-    entityCollection.getEntities().add(new Entity()
-        .addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE))
-        .addProperty(new Property(null, "PropertyStream", ValueType.PRIMITIVE, 
readLink)));
+      entityCollection.getEntities().add(new Entity()
+          .addProperty(createPrimitive("id", 0))
+          .addProperty(createPrimitive("name", "A")));
+
+      entityCollection.getEntities().add(new Entity()
+          .addProperty(createPrimitive("id", 1))
+          .addProperty(createPrimitive("name", "B")));
 
-    Link editLink = new Link();
-    editLink.setRel(Constants.NS_MEDIA_EDIT_LINK_REL);
-    editLink.setHref("http://mediaserver:1234/editLink";);
-    editLink.setMediaETag("eTag");
-    editLink.setType("image/jpeg");
+      entityCollection.getEntities().add(new Entity()
+          .addProperty(createPrimitive("id", 2))
+          .addProperty(createPrimitive("name", "C")));
 
-    entityCollection.getEntities().add(new Entity()
-        .addProperty(createPrimitive("PropertyInt16", (short) 7))
-        .addProperty(new Property(null, "PropertyStream", ValueType.PRIMITIVE, 
editLink)));
+      entityCollection.getEntities().add(new Entity()
+          .addProperty(createPrimitive("id", 3))
+          .addProperty(createPrimitive("name", "D")));
 
-    setEntityType(entityCollection, 
edm.getEntityType(EntityTypeProvider.nameETStream));
-    createEntityId(edm, odata, "ESWithStream", entityCollection);
-    createOperations("ESWithStream", entityCollection, 
EntityTypeProvider.nameETStream);
-    return entityCollection;
+      setEntityType(entityCollection, 
edm.getEntityType(EntityTypeProvider.nameETPeople));
+      createEntityId(edm, odata, "ESPeople", entityCollection);
+      return entityCollection;
   }
   
+  private EntityCollection createESWithStream(final Edm edm, final OData 
odata) {
+      EntityCollection entityCollection = new EntityCollection();
+
+      Link readLink = new Link();
+      readLink.setRel(Constants.NS_MEDIA_READ_LINK_REL);
+      readLink.setHref("readLink");
+      
+      entityCollection.getEntities().add(new Entity()
+          .addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE))
+          .addProperty(new Property(null, "PropertyStream", 
ValueType.PRIMITIVE, readLink)));
+
+      Link editLink = new Link();
+      editLink.setRel(Constants.NS_MEDIA_EDIT_LINK_REL);
+      editLink.setHref("http://mediaserver:1234/editLink";);
+      editLink.setMediaETag("eTag");
+      editLink.setType("image/jpeg");
+
+      entityCollection.getEntities().add(new Entity()
+          .addProperty(createPrimitive("PropertyInt16", (short) 7))
+          .addProperty(new Property(null, "PropertyStream", 
ValueType.PRIMITIVE, editLink)));
+
+      setEntityType(entityCollection, 
edm.getEntityType(EntityTypeProvider.nameETStream));
+      createEntityId(edm, odata, "ESWithStream", entityCollection);
+      createOperations("ESWithStream", entityCollection, 
EntityTypeProvider.nameETStream);
+      return entityCollection;
+    }  
+  
   @SuppressWarnings("unchecked")
   private Property createCollPropertyComp() {
     return createComplexCollection("CollPropertyComp",
@@ -1259,6 +1285,18 @@ public class DataCreator {
         + "    <circle cx=\"50\" cy=\"50\" r=\"42\"/>\n" + "  </g>\n" + 
"</svg>\n").getBytes(Charset.forName("UTF-8"));
   }
 
+  private void linkESPeople(final Map<String, EntityCollection> data) {
+      final EntityCollection entityCollection = data.get("ESPeople");
+      final List<Entity> targetEntities = data.get("ESPeople").getEntities();
+
+      setLinks(entityCollection.getEntities().get(0), "friends", 
targetEntities.get(1),
+          targetEntities.get(2));
+      setLinks(entityCollection.getEntities().get(1), "friends", 
targetEntities.get(0),
+              targetEntities.get(2));
+      setLinks(entityCollection.getEntities().get(2), "friends", 
targetEntities.get(0),
+              targetEntities.get(3));      
+  }
+  
   private void linkESTwoPrim(final Map<String, EntityCollection> data) {
     final EntityCollection entityCollection = data.get("ESTwoPrim");
     final List<Entity> targetEntities = data.get("ESAllPrim").getEntities();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java
index 18fd7a0..cb554f7 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java
@@ -103,6 +103,7 @@ public class ContainerProvider {
     entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, 
"ESTwoKeyNavCont"));
     entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, 
"ESWithStream"));
     entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, 
ES_STREAM));
+    entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, 
"ESPeople"));
 
     // Singletons
     List<CsdlSingleton> singletons = new ArrayList<CsdlSingleton>();
@@ -631,9 +632,14 @@ public class ContainerProvider {
                     .setValue("Entity set with a stream type")),
             new 
CsdlAnnotation().setTerm(TermProvider.TERM_DATA.getFullQualifiedNameAsString()).setExpression(
                 new 
CsdlConstantExpression(CsdlConstantExpression.ConstantExpressionType.Bool, 
"true"))));
+      } else if (name.equals("ESPeople")) {
+        return new CsdlEntitySet()
+          .setName("ESPeople")
+          .setType(EntityTypeProvider.nameETPeople)
+          .setNavigationPropertyBindings(Arrays.asList(new 
CsdlNavigationPropertyBinding().setPath("friends")
+            .setTarget("ESPeople")));
       }
     }
-
     return null;
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java
index eac9743..6e9a1b7 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java
@@ -84,6 +84,7 @@ public class EntityTypeProvider {
   public static final FullQualifiedName nameETStream = new 
FullQualifiedName(SchemaProvider.NAMESPACE,
       "ETWithStream");
   
+  public static final FullQualifiedName nameETPeople = new 
FullQualifiedName(SchemaProvider.NAMESPACE, "ETPeople");
 
   public CsdlEntityType getEntityType(final FullQualifiedName entityTypeName) 
throws ODataException {
     if(entityTypeName.equals(nameETAllPrimDefaultValues)){        
@@ -372,7 +373,7 @@ public class EntityTypeProvider {
           PropertyProvider.navPropertyETTwoKeyNavContOneCT_ETTwoKeyNav,
           
PropertyProvider.collectionNavPropertyETTwoKeyNavContMany_CT_ETTwoKeyNav
           ));
-} else if (entityTypeName.equals(nameETTwoKeyNav)) {
+    } else if (entityTypeName.equals(nameETTwoKeyNav)) {
       return new CsdlEntityType()
           .setName("ETTwoKeyNav")
           .setKey(Arrays.asList(
@@ -472,6 +473,14 @@ public class EntityTypeProvider {
           .setProperties(Arrays.asList(
               PropertyProvider.propertyInt16_NotNullable,
               PropertyProvider.propertyStream));      
+    } else if (entityTypeName.equals(nameETPeople)) {
+        return new CsdlEntityType()
+          .setName(nameETPeople.getName())
+          .setKey(Arrays.asList(new CsdlPropertyRef().setName("id")))
+          .setProperties(Arrays.asList(
+            PropertyProvider.propertyId,
+            PropertyProvider.propertyName))
+          
.setNavigationProperties(Arrays.asList(PropertyProvider.navPropertyFriends));
     }
     return null;
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java
index 3abe050..c19e99f 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java
@@ -927,4 +927,20 @@ public class PropertyProvider {
       .setName("CollPropertyDefString")
       .setType(TypeDefinitionProvider.nameTDString)
       .setCollection(true);
+
+  public static final CsdlProperty propertyId = new CsdlProperty()
+    .setName("id")
+    .setType(nameInt32)
+    .setNullable(false);
+  
+  public static final CsdlProperty propertyName = new CsdlProperty()
+    .setName("name")
+    .setType(nameString)
+    .setNullable(true);  
+  
+  public static final CsdlNavigationProperty navPropertyFriends = new 
CsdlNavigationProperty()
+    .setName("friends")
+    .setType(EntityTypeProvider.nameETPeople)
+    .setNullable(true)
+    .setCollection(true);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java
 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java
index 0daee4d..0c83e40 100644
--- 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java
+++ 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java
@@ -37,7 +37,6 @@ 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.core.edm.primitivetype.EdmDate;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.deserializer.ODataDeserializer;
 import 
org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
index efd0713..82f49aa 100644
--- 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
+++ 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
@@ -391,7 +391,9 @@ public class ODataJsonSerializerTest {
       }
       @Override
       public Entity next() {
-        return new Entity();
+        Entity e =  new Entity();
+        e.setId(URI.create("id"));
+        return e;
       }
     };
     CountOption countOption = Mockito.mock(CountOption.class);
@@ -421,7 +423,7 @@ public class ODataJsonSerializerTest {
     ByteArrayOutputStream bout = new ByteArrayOutputStream();
     result.write(bout);
     final String resultString = new String(bout.toByteArray(), "UTF-8");
-    Assert.assertEquals(resultString, "ERROR: MISSING_PROPERTY");
+    Assert.assertEquals("ERROR: MISSING_PROPERTY", resultString);
   }
 
 
@@ -540,6 +542,7 @@ public class ODataJsonSerializerTest {
   public void nullCollectionButInDataMap() throws Exception {
     final EdmEntitySet edmEntitySet = 
entityContainer.getEntitySet("ESMixEnumDefCollComp");
     Entity entity = new Entity();
+    entity.setId(URI.create("id"));
     entity.addProperty(new Property(null, "PropertyEnumString", 
ValueType.ENUM, 6));
     entity.addProperty(new Property(null, "CollPropertyEnumString", 
ValueType.COLLECTION_ENUM, null));
     entity.addProperty(new Property(null, "PropertyDefString", 
ValueType.PRIMITIVE, "Test"));
@@ -572,6 +575,7 @@ public class ODataJsonSerializerTest {
   public void nullComplexValueButInDataMapAndNullCollectionsNotInDataMap() 
throws Exception {
     final EdmEntitySet edmEntitySet = 
entityContainer.getEntitySet("ESMixEnumDefCollComp");
     Entity entity = new Entity();
+    entity.setId(URI.create("id"));
     entity.addProperty(new Property(null, "PropertyEnumString", 
ValueType.ENUM, 6));
     entity.addProperty(new Property(null, "PropertyDefString", 
ValueType.PRIMITIVE, "Test"));
     entity.addProperty(new Property(null, "PropertyCompMixedEnumDef", 
ValueType.COMPLEX, null));
@@ -594,6 +598,7 @@ public class ODataJsonSerializerTest {
   public void enumAndTypeDefinition() throws Exception {
     final EdmEntitySet edmEntitySet = 
entityContainer.getEntitySet("ESMixEnumDefCollComp");
     Entity entity = new Entity();
+    entity.setId(URI.create("id"));
     entity.addProperty(new Property(null, "PropertyEnumString", 
ValueType.ENUM, 6));
     entity.addProperty(new Property(null, "CollPropertyEnumString", 
ValueType.COLLECTION_ENUM,
         Arrays.asList(2, 4, 6)));
@@ -1903,4 +1908,153 @@ public class ODataJsonSerializerTest {
     }
     Assert.assertEquals(3, count);
   }
+  
+   @Test
+   public void expandCycle() throws Exception {
+     final EdmEntitySet edmEntitySet = 
entityContainer.getEntitySet("ESPeople");
+     final Entity entity = data.readAll(edmEntitySet).getEntities().get(1);
+     ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, 
"friends");
+     LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class);
+     Mockito.when(levels.isMax()).thenReturn(Boolean.TRUE);
+     Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels);
+     final ExpandOption expand = 
ExpandSelectMock.mockExpandOption(Collections.singletonList(
+         mockExpandItem));
+     InputStream result = serializer.entity(metadata, 
edmEntitySet.getEntityType(), entity,
+         EntitySerializerOptions.with()
+             
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
+             .expand(expand)
+             .build()).getContent();
+     final String resultString = IOUtils.toString(result);
+     String expected = "{" + 
+         "\"@odata.context\":\"$metadata#ESPeople/$entity\"," + 
+         "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + 
+         "\"id\":1," + 
+         "\"name\":\"B\"," + 
+         "\"friends\":[" + 
+               "{" + 
+                  "\"id\":0," + 
+                  "\"name\":\"A\"," + 
+                  "\"friends\":[" + 
+                     "{" + 
+                        "\"@odata.id\":\"ESPeople(1)\"" + 
+                     "}," + 
+                     "{" + 
+                        "\"id\":2," + 
+                        "\"name\":\"C\"," + 
+                        "\"friends\":[" + 
+                           "{" + 
+                              "\"@odata.id\":\"ESPeople(0)\"" + 
+                           "}," + 
+                           "{" + 
+                              "\"id\":3," + 
+                              "\"name\":\"D\"," + 
+                              "\"friends\":[" + 
+                              "]" + 
+                           "}" + 
+                        "]" + 
+                     "}" + 
+                  "]" + 
+               "}," + 
+               "{" + 
+                  "\"id\":2," + 
+                  "\"name\":\"C\"," + 
+                  "\"friends\":[" + 
+                     "{" + 
+                        "\"id\":0," + 
+                        "\"name\":\"A\"," + 
+                        "\"friends\":[" + 
+                           "{" + 
+                              "\"@odata.id\":\"ESPeople(1)\"" + 
+                           "}," + 
+                           "{" + 
+                              "\"@odata.id\":\"ESPeople(2)\"" + 
+                           "}" + 
+                        "]" + 
+                     "}," + 
+                     "{" + 
+                        "\"id\":3," + 
+                        "\"name\":\"D\"," + 
+                        "\"friends\":[" + 
+                        "]" + 
+                     "}" + 
+                  "]" + 
+               "}" + 
+            "]" + 
+         "}";
+     Assert.assertEquals(expected, resultString);
+   }
+   
+   @Test
+   public void expandCycleWith3Level() throws Exception {
+     final EdmEntitySet edmEntitySet = 
entityContainer.getEntitySet("ESPeople");
+     final Entity entity = data.readAll(edmEntitySet).getEntities().get(1);
+     ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, 
"friends");
+     LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class);
+     Mockito.when(levels.isMax()).thenReturn(Boolean.FALSE);
+     Mockito.when(levels.getValue()).thenReturn(3);
+     Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels);
+     final ExpandOption expand = 
ExpandSelectMock.mockExpandOption(Collections.singletonList(
+         mockExpandItem));
+     InputStream result = serializer.entity(metadata, 
edmEntitySet.getEntityType(), entity,
+         EntitySerializerOptions.with()
+             
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
+             .expand(expand)
+             .build()).getContent();
+     final String resultString = IOUtils.toString(result);
+     String expected = "{" + 
+       "\"@odata.context\":\"$metadata#ESPeople/$entity\"," + 
+       "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + 
+       "\"id\":1," + 
+       "\"name\":\"B\"," + 
+       "\"friends\":[" + 
+         "{" + 
+           "\"id\":0," + 
+           "\"name\":\"A\"," + 
+           "\"friends\":[" + 
+             "{" + 
+               "\"@odata.id\":\"ESPeople(1)\"" + 
+             "}," + 
+             "{" + 
+               "\"id\":2," + 
+               "\"name\":\"C\"," + 
+               "\"friends\":[" + 
+                 "{" + 
+                   "\"@odata.id\":\"ESPeople(0)\"" + 
+                 "}," + 
+                 "{" + 
+                   "\"id\":3," + 
+                   "\"name\":\"D\"" + 
+                 "}" + 
+               "]" + 
+             "}" + 
+           "]" + 
+         "}," + 
+         "{" + 
+           "\"id\":2," + 
+           "\"name\":\"C\"," + 
+           "\"friends\":[" + 
+             "{" + 
+               "\"id\":0," + 
+               "\"name\":\"A\"," + 
+               "\"friends\":[" + 
+                 "{" + 
+                   "\"@odata.id\":\"ESPeople(1)\"" + 
+                 "}," + 
+                 "{" + 
+                   "\"@odata.id\":\"ESPeople(2)\"" + 
+                 "}" + 
+               "]" + 
+             "}," + 
+             "{" + 
+               "\"id\":3," + 
+               "\"name\":\"D\"," + 
+               "\"friends\":[" + 
+               "]" + 
+             "}" + 
+           "]" + 
+         "}" + 
+       "]" + 
+       "}"; 
+     Assert.assertEquals(expected, resultString);
+   }   
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java
 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java
index 64acb48..7eae959 100644
--- 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java
+++ 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java
@@ -29,9 +29,9 @@ import java.util.Date;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.ComplexValue;
 import org.apache.olingo.commons.api.data.ContextURL;
 import org.apache.olingo.commons.api.data.ContextURL.Suffix;
-import org.apache.olingo.commons.api.data.ComplexValue;
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntityCollection;
 import org.apache.olingo.commons.api.data.Property;
@@ -59,6 +59,7 @@ import org.apache.olingo.server.api.uri.UriHelper;
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
 import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.LevelsExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.SelectItem;
 import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 import org.apache.olingo.server.core.ServiceMetadataImpl;
@@ -724,6 +725,7 @@ public class ODataXmlSerializerTest {
   public void enumAndTypeDefinition() throws Exception {
     final EdmEntitySet edmEntitySet = 
entityContainer.getEntitySet("ESMixEnumDefCollComp");
     Entity entity = new Entity();
+    entity.setId(URI.create("id"));
     entity.addProperty(new Property(null, "PropertyEnumString", 
ValueType.ENUM, 6));
     entity.addProperty(new Property(null, "CollPropertyEnumString", 
ValueType.COLLECTION_ENUM,
         Arrays.asList(2, 4, 6)));
@@ -748,9 +750,11 @@ public class ODataXmlSerializerTest {
         + "<a:entry xmlns:a=\"" + Constants.NS_ATOM + "\""
         + "  xmlns:m=\"" + Constants.NS_METADATA + "\" xmlns:d=\"" + 
Constants.NS_DATASERVICES + "\""
         + " m:context=\"$metadata#ESMixEnumDefCollComp/$entity\" 
m:metadata-etag=\"metadataETag\">\n"
+        + "  <a:id>id</a:id>"
         + "  <a:title /> <a:summary />\n"
         + "  <a:updated>" + UPDATED_FORMAT.format(new Date(currentTimeMillis)) 
+ "</a:updated>\n"
         + "  <a:author> <a:name /> </a:author>\n"
+        + "  <a:link rel=\"edit\" href=\"id\" />"
         + "  <a:category scheme=\"" + Constants.NS_SCHEME + "\"\n"
         + "    term=\"#olingo.odata.test1.ETMixEnumDefCollComp\" />\n"
         + "  <a:content type=\"application/xml\">\n"
@@ -2305,7 +2309,409 @@ public class ODataXmlSerializerTest {
         "</a:feed>";
     checkXMLEqual(expected, resultString);
   }
+  
+  @Test
+  public void expandCycle() throws Exception {
+    final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople");
+    final Entity entity = data.readAll(edmEntitySet).getEntities().get(1);
+    ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, 
"friends");
+    LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class);
+    Mockito.when(levels.isMax()).thenReturn(Boolean.TRUE);
+    Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels);
+    final ExpandOption expand = 
ExpandSelectMock.mockExpandOption(Collections.singletonList(
+        mockExpandItem));
+    long currentTimeMillis = System.currentTimeMillis();
+    SerializerResult result = serializer.entity(metadata, 
edmEntitySet.getEntityType(), entity,
+        EntitySerializerOptions.with()
+            
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
+            .expand(expand)
+            .build());
+    final String resultString = IOUtils.toString(result.getContent());
+
+    String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
+            "<a:entry xmlns:a=\"http://www.w3.org/2005/Atom\"; "
+            + "xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\"; "
+            + "xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\"; "
+            + "m:context=\"$metadata#ESPeople/$entity\" 
m:metadata-etag=\"metadataETag\">\n" + 
+            "   <a:id>ESPeople(1)</a:id>\n" + 
+            "   <a:title />\n" + 
+            "   <a:summary />\n" + 
+            "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+            "   <a:author>\n" + 
+            "      <a:name />\n" + 
+            "   </a:author>\n" + 
+            "   <a:link rel=\"edit\" href=\"ESPeople(1)\" />\n" + 
+            "   <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+            + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(1)/friends\">\n" + 
+            "      <m:inline>\n" + 
+            "         <a:feed>\n" + 
+            "            <a:entry>\n" + 
+            "               <a:id>ESPeople(0)</a:id>\n" + 
+            "               <a:title />\n" + 
+            "               <a:summary />\n" + 
+            "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+            "               <a:author>\n" + 
+            "                  <a:name />\n" + 
+            "               </a:author>\n" + 
+            "               <a:link rel=\"edit\" href=\"ESPeople(0)\" />\n" + 
+            "               <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+            + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(0)/friends\">\n" + 
+            "                  <m:inline>\n" + 
+            "                     <a:feed>\n" + 
+            "                        <m:ref id=\"ESPeople(1)\" />\n" + 
+            "                        <a:entry>\n" + 
+            "                           <a:id>ESPeople(2)</a:id>\n" + 
+            "                           <a:title />\n" + 
+            "                           <a:summary />\n" + 
+            "                     <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+            "                           <a:author>\n" + 
+            "                              <a:name />\n" + 
+            "                           </a:author>\n" + 
+            "                           <a:link rel=\"edit\" 
href=\"ESPeople(2)\" />\n" + 
+            "                           <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+            + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(2)/friends\">\n" + 
+            "                              <m:inline>\n" + 
+            "                                 <a:feed>\n" + 
+            "                                    <m:ref id=\"ESPeople(0)\" 
/>\n" + 
+            "                                    <a:entry>\n" + 
+            "                                       
<a:id>ESPeople(3)</a:id>\n" + 
+            "                                       <a:title />\n" + 
+            "                                       <a:summary />\n" + 
+            "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+            "                                       <a:author>\n" + 
+            "                                          <a:name />\n" + 
+            "                                       </a:author>\n" + 
+            "                                       <a:link rel=\"edit\" 
href=\"ESPeople(3)\" />\n" + 
+            "                                     <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+            + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(3)/friends\">\n" + 
+            "                                          <m:inline>\n" + 
+            "                                             <a:feed />\n" + 
+            "                                          </m:inline>\n" + 
+            "                                       </a:link>\n" + 
+            "                                       <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+            + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+            "                                       <a:content 
type=\"application/xml\">\n" + 
+            "                                          <m:properties>\n" + 
+            "                                             <d:id 
m:type=\"Int32\">3</d:id>\n" + 
+            "                                             
<d:name>D</d:name>\n" + 
+            "                                          </m:properties>\n" + 
+            "                                       </a:content>\n" + 
+            "                                    </a:entry>\n" + 
+            "                                 </a:feed>\n" + 
+            "                              </m:inline>\n" + 
+            "                           </a:link>\n" + 
+            "                           <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+            + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+            "                           <a:content 
type=\"application/xml\">\n" + 
+            "                              <m:properties>\n" + 
+            "                                 <d:id 
m:type=\"Int32\">2</d:id>\n" + 
+            "                                 <d:name>C</d:name>\n" + 
+            "                              </m:properties>\n" + 
+            "                           </a:content>\n" + 
+            "                        </a:entry>\n" + 
+            "                     </a:feed>\n" + 
+            "                  </m:inline>\n" + 
+            "               </a:link>\n" + 
+            "               <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+            + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+            "               <a:content type=\"application/xml\">\n" + 
+            "                  <m:properties>\n" + 
+            "                     <d:id m:type=\"Int32\">0</d:id>\n" + 
+            "                     <d:name>A</d:name>\n" + 
+            "                  </m:properties>\n" + 
+            "               </a:content>\n" + 
+            "            </a:entry>\n" + 
+            "            <a:entry>\n" + 
+            "               <a:id>ESPeople(2)</a:id>\n" + 
+            "               <a:title />\n" + 
+            "               <a:summary />\n" + 
+            "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+            "               <a:author>\n" + 
+            "                  <a:name />\n" + 
+            "               </a:author>\n" + 
+            "               <a:link rel=\"edit\" href=\"ESPeople(2)\" />\n" + 
+            "               <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+            + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(2)/friends\">\n" + 
+            "                  <m:inline>\n" + 
+            "                     <a:feed>\n" + 
+            "                        <a:entry>\n" + 
+            "                           <a:id>ESPeople(0)</a:id>\n" + 
+            "                           <a:title />\n" + 
+            "                           <a:summary />\n" + 
+            "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+            "                           <a:author>\n" + 
+            "                              <a:name />\n" + 
+            "                           </a:author>\n" + 
+            "                           <a:link rel=\"edit\" 
href=\"ESPeople(0)\" />\n" + 
+            "                           <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+            + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(0)/friends\">\n" + 
+            "                              <m:inline>\n" + 
+            "                                 <a:feed>\n" + 
+            "                                    <m:ref id=\"ESPeople(1)\" 
/>\n" + 
+            "                                    <m:ref id=\"ESPeople(2)\" 
/>\n" + 
+            "                                 </a:feed>\n" + 
+            "                              </m:inline>\n" + 
+            "                           </a:link>\n" + 
+            "                           <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+            + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+            "                           <a:content 
type=\"application/xml\">\n" + 
+            "                              <m:properties>\n" + 
+            "                                 <d:id 
m:type=\"Int32\">0</d:id>\n" + 
+            "                                 <d:name>A</d:name>\n" + 
+            "                              </m:properties>\n" + 
+            "                           </a:content>\n" + 
+            "                        </a:entry>\n" + 
+            "                        <a:entry>\n" + 
+            "                           <a:id>ESPeople(3)</a:id>\n" + 
+            "                           <a:title />\n" + 
+            "                           <a:summary />\n" + 
+            "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+            "                           <a:author>\n" + 
+            "                              <a:name />\n" + 
+            "                           </a:author>\n" + 
+            "                           <a:link rel=\"edit\" 
href=\"ESPeople(3)\" />\n" + 
+            "                           <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+            + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(3)/friends\">\n" + 
+            "                              <m:inline>\n" + 
+            "                                 <a:feed />\n" + 
+            "                              </m:inline>\n" + 
+            "                           </a:link>\n" + 
+            "                           <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+            + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+            "                           <a:content 
type=\"application/xml\">\n" + 
+            "                              <m:properties>\n" + 
+            "                                 <d:id 
m:type=\"Int32\">3</d:id>\n" + 
+            "                                 <d:name>D</d:name>\n" + 
+            "                              </m:properties>\n" + 
+            "                           </a:content>\n" + 
+            "                        </a:entry>\n" + 
+            "                     </a:feed>\n" + 
+            "                  </m:inline>\n" + 
+            "               </a:link>\n" + 
+            "               <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+            + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+            "               <a:content type=\"application/xml\">\n" + 
+            "                  <m:properties>\n" + 
+            "                     <d:id m:type=\"Int32\">2</d:id>\n" + 
+            "                     <d:name>C</d:name>\n" + 
+            "                  </m:properties>\n" + 
+            "               </a:content>\n" + 
+            "            </a:entry>\n" + 
+            "         </a:feed>\n" + 
+            "      </m:inline>\n" + 
+            "   </a:link>\n" + 
+            "   <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+            + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+            "   <a:content type=\"application/xml\">\n" + 
+            "      <m:properties>\n" + 
+            "         <d:id m:type=\"Int32\">1</d:id>\n" + 
+            "         <d:name>B</d:name>\n" + 
+            "      </m:properties>\n" + 
+            "   </a:content>\n" + 
+            "</a:entry>\n" + 
+            "";
+    checkXMLEqual(expected, resultString);
+  }  
 
+  @Test
+  public void expandCycleWith3Level() throws Exception {
+    final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople");
+    final Entity entity = data.readAll(edmEntitySet).getEntities().get(1);
+    ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, 
"friends");
+    LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class);
+    Mockito.when(levels.isMax()).thenReturn(Boolean.FALSE);
+    Mockito.when(levels.getValue()).thenReturn(3);
+    Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels);
+    final ExpandOption expand = 
ExpandSelectMock.mockExpandOption(Collections.singletonList(
+        mockExpandItem));
+    long currentTimeMillis = System.currentTimeMillis();
+    InputStream result = serializer.entity(metadata, 
edmEntitySet.getEntityType(), entity,
+        EntitySerializerOptions.with()
+            
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
+            .expand(expand)
+            .build()).getContent();
+    final String resultString = IOUtils.toString(result);
+    String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
+        "<a:entry xmlns:a=\"http://www.w3.org/2005/Atom\"; "
+        + "xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\"; "
+        + "xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\"; "
+        + "m:context=\"$metadata#ESPeople/$entity\" 
m:metadata-etag=\"metadataETag\">\n" + 
+        "  <a:id>ESPeople(1)</a:id>\n" + 
+        "  <a:title />\n" + 
+        "  <a:summary />\n" + 
+        "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+        "  <a:author>\n" + 
+        "    <a:name />\n" + 
+        "  </a:author>\n" + 
+        "  <a:link rel=\"edit\" href=\"ESPeople(1)\" />\n" + 
+        "  <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; 
"
+        + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(1)/friends\">\n" + 
+        "    <m:inline>\n" + 
+        "      <a:feed>\n" + 
+        "        <a:entry>\n" + 
+        "          <a:id>ESPeople(0)</a:id>\n" + 
+        "          <a:title />\n" + 
+        "          <a:summary />\n" + 
+        "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+        "          <a:author>\n" + 
+        "            <a:name />\n" + 
+        "          </a:author>\n" + 
+        "          <a:link rel=\"edit\" href=\"ESPeople(0)\" />\n" + 
+        "          <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+        + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(0)/friends\">\n" + 
+        "            <m:inline>\n" + 
+        "              <a:feed>\n" + 
+        "                <m:ref id=\"ESPeople(1)\" />\n" + 
+        "                <a:entry>\n" + 
+        "                  <a:id>ESPeople(2)</a:id>\n" + 
+        "                  <a:title />\n" + 
+        "                  <a:summary />\n" + 
+        "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+        "                  <a:author>\n" + 
+        "                    <a:name />\n" + 
+        "                  </a:author>\n" + 
+        "                  <a:link rel=\"edit\" href=\"ESPeople(2)\" />\n" + 
+        "                  <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+        + "type=\"application/atom+xml;type=feed\""
+        + " title=\"friends\" href=\"ESPeople(2)/friends\">\n" + 
+        "                    <m:inline>\n" + 
+        "                      <a:feed>\n" + 
+        "                        <m:ref id=\"ESPeople(0)\" />\n" + 
+        "                        <a:entry>\n" + 
+        "                          <a:id>ESPeople(3)</a:id>\n" + 
+        "                          <a:title />\n" + 
+        "                          <a:summary />\n" + 
+        "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+        "                          <a:author>\n" + 
+        "                            <a:name />\n" + 
+        "                          </a:author>\n" + 
+        "                          <a:link rel=\"edit\" href=\"ESPeople(3)\" 
/>\n" + 
+        "                          <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+        + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(3)/friends\" />\n" + 
+        "                          <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+        + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+        "                          <a:content type=\"application/xml\">\n" + 
+        "                            <m:properties>\n" + 
+        "                              <d:id m:type=\"Int32\">3</d:id>\n" + 
+        "                              <d:name>D</d:name>\n" + 
+        "                            </m:properties>\n" + 
+        "                          </a:content>\n" + 
+        "                        </a:entry>\n" + 
+        "                      </a:feed>\n" + 
+        "                    </m:inline>\n" + 
+        "                  </a:link>\n" + 
+        "                  <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+        + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+        "                  <a:content type=\"application/xml\">\n" + 
+        "                    <m:properties>\n" + 
+        "                      <d:id m:type=\"Int32\">2</d:id>\n" + 
+        "                      <d:name>C</d:name>\n" + 
+        "                    </m:properties>\n" + 
+        "                  </a:content>\n" + 
+        "                </a:entry>\n" + 
+        "              </a:feed>\n" + 
+        "            </m:inline>\n" + 
+        "          </a:link>\n" + 
+        "          <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+        + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+        "          <a:content type=\"application/xml\">\n" + 
+        "            <m:properties>\n" + 
+        "              <d:id m:type=\"Int32\">0</d:id>\n" + 
+        "              <d:name>A</d:name>\n" + 
+        "            </m:properties>\n" + 
+        "          </a:content>\n" + 
+        "        </a:entry>\n" + 
+        "        <a:entry>\n" + 
+        "          <a:id>ESPeople(2)</a:id>\n" + 
+        "          <a:title />\n" + 
+        "          <a:summary />\n" + 
+        "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+        "          <a:author>\n" + 
+        "            <a:name />\n" + 
+        "          </a:author>\n" + 
+        "          <a:link rel=\"edit\" href=\"ESPeople(2)\" />\n" + 
+        "          <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+        + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(2)/friends\">\n" + 
+        "            <m:inline>\n" + 
+        "              <a:feed>\n" + 
+        "                <a:entry>\n" + 
+        "                  <a:id>ESPeople(0)</a:id>\n" + 
+        "                  <a:title />\n" + 
+        "                  <a:summary />\n" + 
+        "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+        "                  <a:author>\n" + 
+        "                    <a:name />\n" + 
+        "                  </a:author>\n" + 
+        "                  <a:link rel=\"edit\" href=\"ESPeople(0)\" />\n" + 
+        "                  <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+        + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(0)/friends\">\n" + 
+        "                    <m:inline>\n" + 
+        "                      <a:feed>\n" + 
+        "                        <m:ref id=\"ESPeople(1)\" />\n" + 
+        "                        <m:ref id=\"ESPeople(2)\" />\n" + 
+        "                      </a:feed>\n" + 
+        "                    </m:inline>\n" + 
+        "                  </a:link>\n" + 
+        "                  <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+        + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+        "                  <a:content type=\"application/xml\">\n" + 
+        "                    <m:properties>\n" + 
+        "                      <d:id m:type=\"Int32\">0</d:id>\n" + 
+        "                      <d:name>A</d:name>\n" + 
+        "                    </m:properties>\n" + 
+        "                  </a:content>\n" + 
+        "                </a:entry>\n" + 
+        "                <a:entry>\n" + 
+        "                  <a:id>ESPeople(3)</a:id>\n" + 
+        "                  <a:title />\n" + 
+        "                  <a:summary />\n" + 
+        "   <a:updated>"+UPDATED_FORMAT.format(new 
Date(currentTimeMillis))+"</a:updated>\n" + 
+        "                  <a:author>\n" + 
+        "                    <a:name />\n" + 
+        "                  </a:author>\n" + 
+        "                  <a:link rel=\"edit\" href=\"ESPeople(3)\" />\n" + 
+        "                  <a:link 
rel=\"http://docs.oasis-open.org/odata/ns/related/friends\"; "
+        + "type=\"application/atom+xml;type=feed\" title=\"friends\" 
href=\"ESPeople(3)/friends\">\n" + 
+        "                    <m:inline>\n" + 
+        "                      <a:feed />\n" + 
+        "                    </m:inline>\n" + 
+        "                  </a:link>\n" + 
+        "                  <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+        + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+        "                  <a:content type=\"application/xml\">\n" + 
+        "                    <m:properties>\n" + 
+        "                      <d:id m:type=\"Int32\">3</d:id>\n" + 
+        "                      <d:name>D</d:name>\n" + 
+        "                    </m:properties>\n" + 
+        "                  </a:content>\n" + 
+        "                </a:entry>\n" + 
+        "              </a:feed>\n" + 
+        "            </m:inline>\n" + 
+        "          </a:link>\n" + 
+        "          <a:category 
scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+        + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+        "          <a:content type=\"application/xml\">\n" + 
+        "            <m:properties>\n" + 
+        "              <d:id m:type=\"Int32\">2</d:id>\n" + 
+        "              <d:name>C</d:name>\n" + 
+        "            </m:properties>\n" + 
+        "          </a:content>\n" + 
+        "        </a:entry>\n" + 
+        "      </a:feed>\n" + 
+        "    </m:inline>\n" + 
+        "  </a:link>\n" + 
+        "  <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\"; "
+        + "term=\"#olingo.odata.test1.ETPeople\" />\n" + 
+        "  <a:content type=\"application/xml\">\n" + 
+        "    <m:properties>\n" + 
+        "      <d:id m:type=\"Int32\">1</d:id>\n" + 
+        "      <d:name>B</d:name>\n" + 
+        "    </m:properties>\n" + 
+        "  </a:content>\n" + 
+        "</a:entry>";
+    checkXMLEqual(expected, resultString);
+  }
+  
   private void checkXMLEqual(final String expected, final String resultString) 
throws SAXException, IOException {
     Diff diff = XMLUnit.compareXML(expected, resultString);
     diff.overrideDifferenceListener(DIFFERENCE_LISTENER);

Reply via email to