You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2015/08/04 16:57:40 UTC

[04/13] olingo-odata4 git commit: OLINGO-640: Adding support for ATOM+XML Serializer and De-Serializer

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/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
new file mode 100644
index 0000000..de50032
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java
@@ -0,0 +1,1038 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.xml;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.ODataErrorDetail;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntityCollection;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Linked;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmString;
+import org.apache.olingo.server.api.ODataServerError;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
+import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
+import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.serializer.SerializerResult;
+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.SelectOption;
+import org.apache.olingo.server.core.serializer.SerializerResultImpl;
+import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
+import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
+import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ODataXmlSerializer implements ODataSerializer {
+  private static final String DATA = "d";
+  private static final String CONTEXT = "context";
+  /** The default character set is UTF-8. */
+  public static final String DEFAULT_CHARSET = "UTF-8";
+  private static final String ATOM = "a";
+  private static final String NS_ATOM = "http://www.w3.org/2005/Atom";
+  private static final String METADATA = "m";
+  private static final String NS_METADATA = "http://docs.oasis-open.org/odata/ns/metadata";
+  private static final String NS_DATA = "http://docs.oasis-open.org/odata/ns/data";
+  private static final String NS_SCHEMA = "http://docs.oasis-open.org/odata/ns/scheme";
+  
+  private static final Logger log = LoggerFactory.getLogger(ODataXmlSerializer.class);
+
+  @Override
+  public SerializerResult serviceDocument(final ServiceMetadata metadata, final String serviceRoot)
+      throws SerializerException {
+    CircleStreamBuffer buffer;
+    XMLStreamWriter xmlStreamWriter = null;
+
+    try {
+      buffer = new CircleStreamBuffer();
+      xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), 
+          DEFAULT_CHARSET);
+      ServiceDocumentXmlSerializer serializer = new ServiceDocumentXmlSerializer(metadata, serviceRoot);
+      serializer.writeServiceDocument(xmlStreamWriter);
+      xmlStreamWriter.flush();
+      xmlStreamWriter.close();
+
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      log.error(e.getMessage(), e);
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    } finally {
+      if (xmlStreamWriter != null) {
+        try {
+          xmlStreamWriter.close();
+        } catch (XMLStreamException e) {
+          throw new SerializerException("An I/O exception occurred.", e,
+              SerializerException.MessageKeys.IO_EXCEPTION);
+        }
+      }
+    }
+  }
+
+  @Override
+  public SerializerResult metadataDocument(final ServiceMetadata serviceMetadata) throws SerializerException {
+    CircleStreamBuffer buffer;
+    XMLStreamWriter xmlStreamWriter = null;
+
+    try {
+      buffer = new CircleStreamBuffer();
+      xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), 
+          DEFAULT_CHARSET);
+      MetadataDocumentXmlSerializer serializer = new MetadataDocumentXmlSerializer(serviceMetadata);
+      serializer.writeMetadataDocument(xmlStreamWriter);
+      xmlStreamWriter.flush();
+      xmlStreamWriter.close();
+
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      log.error(e.getMessage(), e);
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    } finally {
+      if (xmlStreamWriter != null) {
+        try {
+          xmlStreamWriter.close();
+        } catch (XMLStreamException e) {
+          throw new SerializerException("An I/O exception occurred.", e,
+              SerializerException.MessageKeys.IO_EXCEPTION);
+        }
+      }
+    }
+  }
+
+  @Override
+  public SerializerResult error(final ODataServerError error) throws SerializerException {
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+    
+    if (error == null) {
+      throw new SerializerException("ODataError object MUST NOT be null!",
+          SerializerException.MessageKeys.NULL_INPUT);
+    }
+    
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), 
+          DEFAULT_CHARSET);
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      
+      writer.writeStartElement("error");
+      writer.writeDefaultNamespace(NS_METADATA);
+      writeErrorDetails(String.valueOf(error.getStatusCode()), error.getMessage(), error.getTarget(), writer);
+      if (error.getDetails() != null && !error.getDetails().isEmpty()) {
+        writer.writeStartElement("details");
+        for (ODataErrorDetail inner:error.getDetails()) {
+          writeErrorDetails(inner.getCode(), inner.getMessage(), inner.getTarget(), writer);
+        }
+        writer.writeEndElement();
+      }
+      writer.writeEndElement();      
+      writer.writeEndDocument();
+      
+      writer.flush();
+      writer.close();
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    }
+  }
+
+  private void writeErrorDetails(String code, String message, String target, XMLStreamWriter writer)
+      throws XMLStreamException {
+    if (code != null) {
+      writer.writeStartElement("code");
+      writer.writeCharacters(String.valueOf(code));
+      writer.writeEndElement();
+    }
+    
+    writer.writeStartElement("message");
+    writer.writeCharacters(message);
+    writer.writeEndElement();
+
+    if (target != null) {
+      writer.writeStartElement("target");
+      writer.writeCharacters(target);
+      writer.writeEndElement();        
+    }
+  }
+  
+  @Override
+  public SerializerResult entityCollection(final ServiceMetadata metadata,
+      final EdmEntityType entityType, final EntityCollection entitySet,
+      final EntityCollectionSerializerOptions options) throws SerializerException {
+    
+    final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
+    if (options.onlyReferences()) {
+      ReferenceCollectionSerializerOptions rso = ReferenceCollectionSerializerOptions.with()
+          .contextURL(contextURL).build();
+      return entityReferenceCollection(metadata, entityType, entitySet, rso);
+    }
+    
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      writer.writeStartElement(ATOM, "feed", NS_ATOM);
+      writer.writeNamespace(ATOM, NS_ATOM);
+      writer.writeNamespace(METADATA, NS_METADATA);
+      writer.writeNamespace(DATA, NS_DATA);
+      
+      writer.writeAttribute(METADATA, NS_METADATA, "context", 
+          ContextURLBuilder.create(contextURL).toASCIIString());
+      writeMetadataETag(metadata, writer);
+      
+      writer.writeStartElement(ATOM, "id", NS_ATOM);
+      writer.writeCharacters(options.getId());
+      writer.writeEndElement();
+
+      if (options != null && options.getCount() != null && options.getCount().getValue()
+          && entitySet.getCount() != null) {
+        writeCount(entitySet, writer);
+      }
+      if (entitySet.getNext() != null) {
+        writeNextLink(entitySet, writer);
+      }
+      
+      writeEntitySet(metadata, entityType, entitySet,
+          options.getExpand(), options.getSelect(), options.onlyReferences(), writer);
+
+      writer.writeEndElement();
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    }
+  }
+
+  @Override
+  public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType,
+      final Entity entity, final EntitySerializerOptions options) throws SerializerException {
+    final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
+    
+    if (options.onlyReferences()) {
+      ReferenceSerializerOptions rso = ReferenceSerializerOptions.with()
+          .contextURL(contextURL).build();
+      return entityReference(metadata, entityType, entity, rso);
+    }
+    
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      writeEntity(metadata, entityType, entity, contextURL,
+          options == null ? null : options.getExpand(),
+          options == null ? null : options.getSelect(),
+          options == null ? false : options.onlyReferences(), writer, true);
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    }
+  }
+
+  private ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException {
+    if (contextURL == null) {
+      throw new SerializerException("ContextURL null!", SerializerException.MessageKeys.NO_CONTEXT_URL);
+    }
+    return contextURL;
+  }
+
+  private void writeMetadataETag(final ServiceMetadata metadata, XMLStreamWriter writer) 
+      throws XMLStreamException {
+    if (metadata != null
+        && metadata.getServiceMetadataETagSupport() != null
+        && metadata.getServiceMetadataETagSupport().getMetadataETag() != null) {
+      writer.writeAttribute(METADATA, NS_METADATA, "metadata-etag", 
+          metadata.getServiceMetadataETagSupport().getMetadataETag());
+    }
+  }
+
+  protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
+      final EntityCollection entitySet, final ExpandOption expand, final SelectOption select,
+      final boolean onlyReference, final XMLStreamWriter writer) throws XMLStreamException,
+      SerializerException {
+    for (final Entity entity : entitySet.getEntities()) {
+      writeEntity(metadata, entityType, entity, null, expand, select, false, writer, false);
+    }
+  }
+
+  protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType,
+      final Entity entity, final ContextURL contextURL, final ExpandOption expand,
+      final SelectOption select, final boolean onlyReference, final XMLStreamWriter writer, 
+      final boolean top) throws XMLStreamException, SerializerException {
+
+    writer.writeStartElement(ATOM, "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, CONTEXT, 
+            ContextURLBuilder.create(contextURL).toASCIIString());
+        writeMetadataETag(metadata, writer);
+  
+      }
+    }
+    if (entity.getETag() != null) {
+      writer.writeAttribute(METADATA, NS_METADATA, "etag", entity.getETag());
+    }
+    
+    writer.writeStartElement(NS_ATOM, "id");
+    writer.writeCharacters(entity.getId().toASCIIString());
+    writer.writeEndElement();
+
+    writerAuthorInfo(entity.getTitle(), writer);
+    
+    writer.writeStartElement(NS_ATOM, "link");
+    writer.writeAttribute("rel", "edit");
+    writer.writeAttribute("href", entity.getId().toASCIIString());
+    writer.writeEndElement();
+
+    if (entityType.hasStream()) {
+      writer.writeStartElement(NS_ATOM, "content");
+      writer.writeAttribute("type", entity.getMediaContentType());
+      if (entity.getMediaContentSource() != null) {
+        writer.writeAttribute("src", entity.getMediaContentSource().toString());
+      } else {
+        String id = entity.getId().toASCIIString();
+        if (id.endsWith("/")) {
+          writer.writeAttribute("src", id+"$value");
+        } else {
+          writer.writeAttribute("src", id+"/$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, writer);
+    
+    writer.writeStartElement(ATOM, "category", NS_ATOM);
+    writer.writeAttribute("scheme", NS_SCHEMA);
+    writer.writeAttribute("term", "#"+resolvedType.getFullQualifiedName().getFullQualifiedNameAsString());
+    writer.writeEndElement();
+    
+    // In the case media, content is sibiling
+    if (!entityType.hasStream()) {
+      writer.writeStartElement(NS_ATOM, "content");
+      writer.writeAttribute("type", "application/xml");
+    }
+    
+    writer.writeStartElement(METADATA, "properties", NS_METADATA);
+    writeProperties(metadata, resolvedType, entity.getProperties(), select, writer);
+    writer.writeEndElement(); // properties
+    
+    if (!entityType.hasStream()) { // content
+      writer.writeEndElement();
+    }
+    writer.writeEndElement(); //entry
+  }
+
+  private void writerAuthorInfo(final String title, final XMLStreamWriter writer) throws XMLStreamException {
+    writer.writeStartElement(NS_ATOM, "title");
+    if (title != null) {
+      writer.writeCharacters(title);
+    }
+    writer.writeEndElement();
+    writer.writeStartElement(NS_ATOM, "summary");
+    writer.writeEndElement();
+    
+    writer.writeStartElement(NS_ATOM, "updated");
+    writer.writeCharacters(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
+    .format(new Date(System.currentTimeMillis())));
+    writer.writeEndElement();
+    
+    writer.writeStartElement(NS_ATOM, "author");
+    writer.writeStartElement(NS_ATOM, "name");
+    writer.writeEndElement();
+    writer.writeEndElement();
+  }
+
+  protected EdmEntityType resolveEntityType(final ServiceMetadata metadata, final EdmEntityType baseType,
+      final String derivedTypeName) throws SerializerException {
+    if (derivedTypeName == null ||
+        baseType.getFullQualifiedName().getFullQualifiedNameAsString().equals(derivedTypeName)) {
+      return baseType;
+    }
+    EdmEntityType derivedType = metadata.getEdm().getEntityType(new FullQualifiedName(derivedTypeName));
+    if (derivedType == null) {
+      throw new SerializerException("EntityType not found",
+          SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
+    }
+    EdmEntityType type = derivedType.getBaseType();
+    while (type != null) {
+      if (type.getFullQualifiedName().getFullQualifiedNameAsString()
+          .equals(baseType.getFullQualifiedName().getFullQualifiedNameAsString())) {
+        return derivedType;
+      }
+      type = type.getBaseType();
+    }
+    throw new SerializerException("Wrong base type",
+        SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName, baseType
+            .getFullQualifiedName().getFullQualifiedNameAsString());
+  }
+
+  protected EdmComplexType resolveComplexType(final ServiceMetadata metadata, final EdmComplexType baseType,
+      final String derivedTypeName) throws SerializerException {
+    if (derivedTypeName == null ||
+        baseType.getFullQualifiedName().getFullQualifiedNameAsString().equals(derivedTypeName)) {
+      return baseType;
+    }
+    EdmComplexType derivedType = metadata.getEdm().getComplexType(new FullQualifiedName(derivedTypeName));
+    if (derivedType == null) {
+      throw new SerializerException("Complex Type not found",
+          SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
+    }
+    EdmComplexType type = derivedType.getBaseType();
+    while (type != null) {
+      if (type.getFullQualifiedName().getFullQualifiedNameAsString()
+          .equals(baseType.getFullQualifiedName().getFullQualifiedNameAsString())) {
+        return derivedType;
+      }
+      type = type.getBaseType();
+    }
+    throw new SerializerException("Wrong base type",
+        SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName, baseType
+            .getFullQualifiedName().getFullQualifiedNameAsString());
+  }
+
+  protected void writeProperties(final ServiceMetadata metadata, final EdmStructuredType type, 
+      final List<Property> properties, final SelectOption select, final XMLStreamWriter writer) 
+          throws XMLStreamException, SerializerException {
+    final boolean all = ExpandSelectHelper.isAll(select);
+    final Set<String> selected = all ? null :
+        ExpandSelectHelper.getSelectedPropertyNames(select.getSelectItems());
+    for (final String propertyName : type.getPropertyNames()) {
+      if (all || selected.contains(propertyName)) {
+        final EdmProperty edmProperty = type.getStructuralProperty(propertyName);
+        final Property property = findProperty(propertyName, properties);
+        final Set<List<String>> selectedPaths = all || edmProperty.isPrimitive() ? null :
+            ExpandSelectHelper.getSelectedPaths(select.getSelectItems(), propertyName);
+        writeProperty(metadata, edmProperty, property, selectedPaths, writer);
+      }
+    }
+  }
+
+  protected void writeNavigationProperties(final ServiceMetadata metadata,
+      final EdmStructuredType type, final Linked linked, final ExpandOption expand,
+      final XMLStreamWriter writer) throws SerializerException, XMLStreamException {
+    if (ExpandSelectHelper.hasExpand(expand)) {
+      final boolean expandAll = ExpandSelectHelper.isExpandAll(expand);
+      final Set<String> expanded = expandAll ? null :
+          ExpandSelectHelper.getExpandedPropertyNames(expand.getExpandItems());
+      for (final String propertyName : type.getNavigationPropertyNames()) {
+        final EdmNavigationProperty property = type.getNavigationProperty(propertyName);
+        final Link navigationLink = getOrCreateLink(linked, propertyName);        
+        if (expandAll || expanded.contains(propertyName)) {
+          final ExpandItem innerOptions = expandAll ? null :
+              ExpandSelectHelper.getExpandItem(expand.getExpandItems(), propertyName);
+          if (innerOptions != null && innerOptions.getLevelsOption() != null) {
+            throw new SerializerException("Expand option $levels is not supported.",
+                SerializerException.MessageKeys.NOT_IMPLEMENTED);
+          }
+          if (navigationLink != null) {
+            writeLink(writer, navigationLink, false);          
+            writer.writeStartElement(METADATA, "inline", NS_METADATA);
+            writeExpandedNavigationProperty(metadata, property, navigationLink,
+                innerOptions == null ? null : innerOptions.getExpandOption(),
+                innerOptions == null ? null : innerOptions.getSelectOption(), 
+                innerOptions == null ? false: innerOptions.isRef(),
+                writer);
+            writer.writeEndElement();            
+            writer.writeEndElement();
+          }
+        } else {
+          writeLink(writer, getOrCreateLink(linked, propertyName));
+        }
+      }
+    } else {
+      for (final String propertyName : type.getNavigationPropertyNames()) {
+        writeLink(writer, getOrCreateLink(linked, propertyName));
+      }      
+    }
+    for (Link link:linked.getAssociationLinks()) {
+      writeLink(writer, link);
+    }      
+  }
+  
+  protected Link getOrCreateLink(final Linked linked, final String navigationPropertyName) 
+      throws XMLStreamException {
+    Link link = linked.getNavigationLink(navigationPropertyName);
+    if (link == null) {
+      link = new Link();
+      link.setRel("http://docs.oasis-open.org/odata/ns/related/"+navigationPropertyName);
+      link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE);
+      link.setTitle(navigationPropertyName);
+      EntityCollection target = new EntityCollection();
+      link.setInlineEntitySet(target);
+      link.setHref(linked.getId().toASCIIString()+"/"+navigationPropertyName);
+    }
+    return link;
+  }  
+  
+  private void writeLink(final XMLStreamWriter writer, final Link link) throws XMLStreamException {
+    writeLink(writer, link, true);
+  }
+  private void writeLink(final XMLStreamWriter writer, final Link link, final boolean close)
+      throws XMLStreamException {    
+    writer.writeStartElement(ATOM, "link", NS_ATOM);
+    writer.writeAttribute("rel", link.getRel());
+    if (link.getType() != null) {
+      writer.writeAttribute("type", link.getType());
+    }
+    if (link.getTitle() != null) {
+      writer.writeAttribute("title", link.getTitle());
+    }
+    writer.writeAttribute("href", link.getHref());
+    if (close) {
+      writer.writeEndElement();
+    }    
+  }  
+
+  protected void writeExpandedNavigationProperty(final ServiceMetadata metadata,
+      final EdmNavigationProperty property, final Link navigationLink,
+      final ExpandOption innerExpand, final SelectOption innerSelect, boolean onlyReference, 
+      final XMLStreamWriter writer)
+      throws XMLStreamException, SerializerException {
+    if (property.isCollection()) {
+      if (navigationLink == null || navigationLink.getInlineEntitySet() == null) {
+        // nothing to write.
+      } else {
+        writer.writeStartElement(ATOM, "feed", NS_ATOM);
+        writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
+            innerSelect, onlyReference, writer);
+        writer.writeEndElement();
+      }
+    } else {
+      if (navigationLink == null || navigationLink.getInlineEntity() == null) {
+        // nothing to write
+      } else {
+        writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null,
+            innerExpand, innerSelect, onlyReference, writer, false);
+      }
+    }
+  }
+
+  protected void writeProperty(final ServiceMetadata metadata, final EdmProperty edmProperty, 
+      final Property property,
+      final Set<List<String>> selectedPaths, final XMLStreamWriter writer) throws XMLStreamException,
+      SerializerException {
+    writer.writeStartElement(DATA, edmProperty.getName(), NS_DATA);
+    if (property == null || property.isNull()) {
+      if (edmProperty.isNullable() == Boolean.FALSE) {
+        throw new SerializerException("Non-nullable property not present!",
+            SerializerException.MessageKeys.MISSING_PROPERTY, edmProperty.getName());
+      } else {
+        writer.writeAttribute(METADATA, NS_METADATA, "null", "true");
+      }
+    } else {
+      writePropertyValue(metadata, edmProperty, property, selectedPaths, writer);
+    }
+    writer.writeEndElement();
+  }
+  
+  private String collectionType(EdmType type) {
+    return "#Collection("+type.getFullQualifiedName().getFullQualifiedNameAsString()+")";
+  }
+  
+  private String complexType(ServiceMetadata metadata, EdmComplexType baseType, String definedType) 
+      throws SerializerException {
+    EdmComplexType type = resolveComplexType(metadata, baseType, definedType);
+    return type.getFullQualifiedName().getFullQualifiedNameAsString();
+  }
+  
+  private String derivedComplexType(ServiceMetadata metadata, EdmComplexType baseType, String definedType) 
+      throws SerializerException {
+    String derived = baseType.getFullQualifiedName().getFullQualifiedNameAsString();
+    if (derived.equals(definedType)) {
+      return null;
+    }
+    return definedType;
+  }  
+
+  private void writePropertyValue(final ServiceMetadata metadata, final EdmProperty edmProperty,
+      final Property property, final Set<List<String>> selectedPaths,
+      final XMLStreamWriter writer) throws XMLStreamException, SerializerException {
+    try {
+      if (edmProperty.isPrimitive()) {
+        if (edmProperty.isCollection()) {
+          writer.writeAttribute(METADATA, NS_METADATA, "type", "#Collection("+edmProperty.getType().getName()+")");
+          writePrimitiveCollection((EdmPrimitiveType) edmProperty.getType(), property,
+              edmProperty.isNullable(), edmProperty.getMaxLength(),
+              edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(),
+              writer);
+        } else {
+          writePrimitive((EdmPrimitiveType) edmProperty.getType(), property,
+              edmProperty.isNullable(), edmProperty.getMaxLength(),
+              edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(),
+              writer);
+        }
+      } else if (edmProperty.isCollection()) {
+        writer.writeAttribute(METADATA, NS_METADATA, "type", collectionType(edmProperty.getType()));
+        writeComplexCollection(metadata, (EdmComplexType) edmProperty.getType(), property, selectedPaths, writer);
+      } else if (property.isComplex()) {
+        writer.writeAttribute(METADATA, NS_METADATA, "type", 
+            "#"+complexType(metadata, (EdmComplexType) edmProperty.getType(), property.getType()));
+        writeComplexValue(metadata, (EdmComplexType) edmProperty.getType(), property.asComplex().getValue(),
+            selectedPaths, writer);
+      } else if (property.isEnum()) {
+        writePrimitive((EdmPrimitiveType) edmProperty.getType(), property,
+            edmProperty.isNullable(), edmProperty.getMaxLength(),
+            edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(),
+            writer);
+      } else {
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, edmProperty.getName());
+      }
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new SerializerException("Wrong value for property!", e,
+          SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+          edmProperty.getName(), property.getValue().toString());
+    }
+  }
+
+  private void writePrimitiveCollection(final EdmPrimitiveType type, final Property property,
+      final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
+      final Boolean isUnicode,
+      final XMLStreamWriter writer) throws XMLStreamException, EdmPrimitiveTypeException, SerializerException {
+    for (Object value : property.asCollection()) {
+      writer.writeStartElement(METADATA, "element", NS_METADATA);
+      switch (property.getValueType()) {
+      case COLLECTION_PRIMITIVE:
+      case COLLECTION_ENUM:
+        writePrimitiveValue(type, value, isNullable, maxLength, precision, scale, isUnicode, writer);
+        break;
+      case COLLECTION_GEOSPATIAL:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
+      default:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
+      }
+      writer.writeEndElement();
+    }
+  }
+
+  private void writeComplexCollection(final ServiceMetadata metadata, final EdmComplexType type, 
+      final Property property, final Set<List<String>> selectedPaths, final XMLStreamWriter writer)
+      throws XMLStreamException, EdmPrimitiveTypeException, SerializerException {
+    for (Object value : property.asCollection()) {
+      writer.writeStartElement(METADATA, "element", NS_METADATA);
+      if (derivedComplexType(metadata, type, property.getType()) != null) {
+        writer.writeAttribute(METADATA, NS_METADATA, "type", property.getType());
+      }
+      switch (property.getValueType()) {
+      case COLLECTION_COMPLEX:
+        writeComplexValue(metadata, type, ((ComplexValue) value).getValue(), selectedPaths, writer);
+        break;
+      default:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
+      }
+      writer.writeEndElement();
+    }
+  }
+
+  private void writePrimitive(final EdmPrimitiveType type, final Property property,
+      final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
+      final Boolean isUnicode, final XMLStreamWriter writer)
+      throws EdmPrimitiveTypeException, XMLStreamException, SerializerException {
+    if (property.isPrimitive()) {
+      if (!(type instanceof EdmString)) {
+        writer.writeAttribute(METADATA, NS_METADATA, "type", type.getName());
+      }
+      writePrimitiveValue(type, property.asPrimitive(),
+          isNullable, maxLength, precision, scale, isUnicode, writer);
+    } else if (property.isGeospatial()) {
+      throw new SerializerException("Property type not yet supported!",
+          SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
+    } else if (property.isEnum()) {
+      writer.writeAttribute(METADATA, NS_METADATA, "type", "#"+type.getName());
+      writePrimitiveValue(type, property.asEnum(),
+          isNullable, maxLength, precision, scale, isUnicode, writer);
+    } else {
+      throw new SerializerException("Inconsistent property type!",
+          SerializerException.MessageKeys.INCONSISTENT_PROPERTY_TYPE, property.getName());
+    }
+  }
+
+  protected void writePrimitiveValue(final EdmPrimitiveType type, final Object primitiveValue,
+      final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
+      final Boolean isUnicode,
+      final XMLStreamWriter writer) throws EdmPrimitiveTypeException, XMLStreamException {
+    final String value = type.valueToString(primitiveValue,
+        isNullable, maxLength, precision, scale, isUnicode);
+    if (value == null) {
+      writer.writeAttribute(DATA, NS_DATA, "null", "true");
+    } else if(type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) {
+        writer.writeCharacters(value);
+    } else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte)
+        || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)
+        || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double)
+        || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16)
+        || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32)
+        || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64)
+        || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)
+        || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single)) {
+      writer.writeCharacters(value);
+    } else {
+      writer.writeCharacters(value);
+    }
+  }
+
+  protected void writeComplexValue(final ServiceMetadata metadata, final EdmComplexType type, 
+      final List<Property> properties, final Set<List<String>> selectedPaths, final XMLStreamWriter writer)
+      throws XMLStreamException, EdmPrimitiveTypeException, SerializerException {
+    for (final String propertyName : type.getPropertyNames()) {
+      final Property property = findProperty(propertyName, properties);
+      if (selectedPaths == null || ExpandSelectHelper.isSelected(selectedPaths, propertyName)) {
+        writeProperty(metadata, (EdmProperty) type.getProperty(propertyName), property,
+            selectedPaths == null ? null : ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName),
+            writer);
+      }
+    }
+  }
+
+  private Property findProperty(final String propertyName, final List<Property> properties) {
+    for (final Property property : properties) {
+      if (propertyName.equals(property.getName())) {
+        return property;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public SerializerResult primitive(final ServiceMetadata metadata, final EdmPrimitiveType type,
+      final Property property, final PrimitiveSerializerOptions options) throws SerializerException {
+    final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      writer.writeStartElement(METADATA, "value", NS_METADATA);
+      writer.writeNamespace(METADATA, NS_METADATA);
+      if (contextURL != null) {
+        writer.writeAttribute(METADATA, NS_METADATA, CONTEXT, 
+            ContextURLBuilder.create(contextURL).toASCIIString());
+      }
+      writeMetadataETag(metadata, writer);
+      if(property.isNull()) {
+        writer.writeAttribute(METADATA, NS_METADATA, "null", "true");
+      } else {
+        writePrimitive(type, property,
+            options.isNullable(), options.getMaxLength(), options.getPrecision(), options.getScale(),
+            options.isUnicode(),
+            writer);
+      }
+      writer.writeEndElement();
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+  
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new SerializerException("Wrong value for property!", e,
+          SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+          property.getName(), property.getValue().toString());
+    } finally {
+      if (writer != null) {
+        try {
+          writer.close();
+        } catch (XMLStreamException e) {
+          throw new SerializerException("Wrong value for property!", e,
+              SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+              property.getName(), property.getValue().toString());
+        }
+      }
+    }    
+  }
+
+  @Override
+  public SerializerResult complex(final ServiceMetadata metadata, final EdmComplexType type,
+      final Property property, final ComplexSerializerOptions options) throws SerializerException {
+    final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+    try {
+      EdmComplexType resolvedType = resolveComplexType(metadata, type, property.getType());
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      writer.writeStartElement(METADATA, "value", NS_METADATA);
+      writer.writeNamespace(METADATA, NS_METADATA);
+      writer.writeNamespace(DATA, NS_DATA);
+      writer.writeAttribute(METADATA, NS_METADATA, "type", "#"
+          + resolvedType.getFullQualifiedName().getFullQualifiedNameAsString());
+      writer.writeAttribute(METADATA, NS_METADATA, CONTEXT, 
+          ContextURLBuilder.create(contextURL).toASCIIString());
+      writeMetadataETag(metadata, writer);
+      if (property.isNull()) {
+        writer.writeAttribute(METADATA, NS_METADATA, "null", "true");
+      } else {
+        final List<Property> values = property.asComplex().getValue();
+        writeProperties(metadata, resolvedType, values, options == null ? null : options.getSelect(), writer);
+      }
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    }
+  }
+
+  @Override
+  public SerializerResult primitiveCollection(final ServiceMetadata metadata, final EdmPrimitiveType type,
+      final Property property, final PrimitiveSerializerOptions options) throws SerializerException {
+    final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      writer.writeStartElement(METADATA, "value", NS_METADATA);
+      writer.writeNamespace(METADATA, NS_METADATA);
+      if (contextURL != null) {
+        writer.writeAttribute(METADATA, NS_METADATA, CONTEXT, 
+            ContextURLBuilder.create(contextURL).toASCIIString());
+      }      
+      writeMetadataETag(metadata, writer);
+      writer.writeAttribute(METADATA, NS_METADATA, "type", "#Collection("+type.getName()+")");
+      writePrimitiveCollection(type, property,
+          options.isNullable(), options.getMaxLength(), options.getPrecision(), options.getScale(),
+          options.isUnicode(),
+          writer);
+      writer.writeEndElement();
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+  
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new SerializerException("Wrong value for property!", e,
+          SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+          property.getName(), property.getValue().toString());
+    } finally {
+      if (writer != null) {
+        try {
+          writer.close();
+        } catch (XMLStreamException e) {
+          throw new SerializerException("Wrong value for property!", e,
+              SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+              property.getName(), property.getValue().toString());
+        }
+      }
+    }     
+  }
+
+  @Override
+  public SerializerResult complexCollection(final ServiceMetadata metadata, final EdmComplexType type,
+      final Property property, final ComplexSerializerOptions options) throws SerializerException {
+    final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+
+      writer.writeStartElement(METADATA, "value", NS_METADATA);
+      writer.writeNamespace(METADATA, NS_METADATA);
+      writer.writeNamespace(DATA, NS_DATA);
+      writer.writeAttribute(METADATA, NS_METADATA, "type", collectionType(type));      
+      writer.writeAttribute(METADATA, NS_METADATA, CONTEXT, 
+          ContextURLBuilder.create(contextURL).toASCIIString());
+      writeMetadataETag(metadata, writer);
+      writeComplexCollection(metadata, type, property, null, writer);
+      writer.writeEndElement();
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new SerializerException("Wrong value for property!", e,
+          SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+          property.getName(), property.getValue().toString());
+    }
+  }
+
+  @Override
+  public SerializerResult reference(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, 
+      final Entity entity, final ReferenceSerializerOptions options) throws SerializerException {
+    return entityReference(metadata, edmEntitySet.getEntityType(), entity, options);
+  }
+  
+  protected SerializerResult entityReference(final ServiceMetadata metadata, final EdmEntityType entityType,
+      final Entity entity, ReferenceSerializerOptions options) throws SerializerException {
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      writeReference(metadata, entityType, entity, options.getContextURL(), writer, true);
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    }
+  }
+  
+  private void writeReference(ServiceMetadata metadata, EdmEntityType entityType, 
+      Entity entity, ContextURL contextURL, XMLStreamWriter writer, boolean top) throws XMLStreamException {
+    writer.writeStartElement(METADATA, "ref", NS_METADATA);
+    if (top) {
+      writer.writeNamespace(METADATA, NS_METADATA);
+      if (contextURL != null) { // top-level entity
+        writer.writeAttribute(METADATA, NS_METADATA, CONTEXT, 
+            ContextURLBuilder.create(contextURL).toASCIIString());
+      }
+    }
+    writer.writeAttribute("id", entity.getId().toASCIIString());
+    writer.writeEndElement();
+  }
+
+  @Override
+  public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, 
+      final EntityCollection entityCollection, ReferenceCollectionSerializerOptions options) 
+          throws SerializerException {
+    return entityReferenceCollection(metadata, edmEntitySet.getEntityType(), entityCollection, options);
+  }
+  
+  protected SerializerResult entityReferenceCollection(final ServiceMetadata metadata,
+      final EdmEntityType entityType, final EntityCollection entitySet,
+      ReferenceCollectionSerializerOptions options) throws SerializerException {
+    CircleStreamBuffer buffer;
+    XMLStreamWriter writer = null;
+    try {
+      buffer = new CircleStreamBuffer();
+      writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
+      writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+      writer.writeStartElement(ATOM, "feed", NS_ATOM);
+      writer.writeNamespace(ATOM, NS_ATOM);
+      writer.writeNamespace(METADATA, NS_METADATA);
+      if (options.getContextURL() != null) { // top-level entity
+        writer.writeAttribute(METADATA, NS_METADATA, CONTEXT, ContextURLBuilder.create(options.getContextURL())
+            .toASCIIString());
+      }
+      if (options != null && options.getCount() != null && options.getCount().getValue()
+          && entitySet.getCount() != null) {
+        writeCount(entitySet, writer);
+      }
+      if (entitySet.getNext() != null) {
+        writeNextLink(entitySet, writer);
+      }
+      for (final Entity entity : entitySet.getEntities()) {
+        writeReference(metadata, entityType, entity, options.getContextURL(), writer, false);
+      }
+      writer.writeEndElement();
+      writer.writeEndDocument();
+      writer.flush();
+      writer.close();
+      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final XMLStreamException e) {
+      throw new SerializerException("An I/O exception occurred.", e,
+          SerializerException.MessageKeys.IO_EXCEPTION);
+    }
+  }  
+  
+  private void writeCount(final EntityCollection entitySet, XMLStreamWriter writer) 
+      throws XMLStreamException {
+    writer.writeStartElement(METADATA, "count", NS_METADATA);
+    writer.writeCharacters(String.valueOf(entitySet.getCount()));
+    writer.writeEndElement();
+  }
+
+  private void writeNextLink(final EntityCollection entitySet, XMLStreamWriter writer) 
+      throws XMLStreamException {
+    writer.writeStartElement(ATOM, "link", NS_ATOM);
+    writer.writeAttribute("rel", "next");
+    writer.writeAttribute("href", entitySet.getNext().toASCIIString());
+    writer.writeEndElement();
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
deleted file mode 100644
index 1e7349b..0000000
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.server.core.serializer.xml;
-
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-
-import org.apache.olingo.commons.api.data.Entity;
-import org.apache.olingo.commons.api.data.EntityCollection;
-import org.apache.olingo.commons.api.data.Property;
-import org.apache.olingo.commons.api.edm.EdmComplexType;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
-import org.apache.olingo.commons.api.edm.EdmEntityType;
-import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
-import org.apache.olingo.server.api.ODataServerError;
-import org.apache.olingo.server.api.ServiceMetadata;
-import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
-import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
-import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
-import org.apache.olingo.server.api.serializer.ODataSerializer;
-import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
-import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOptions;
-import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
-import org.apache.olingo.server.api.serializer.SerializerException;
-import org.apache.olingo.server.api.serializer.SerializerResult;
-import org.apache.olingo.server.core.serializer.SerializerResultImpl;
-import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ODataXmlSerializerImpl implements ODataSerializer {
-
-  /** The default character set is UTF-8. */
-  public static final String DEFAULT_CHARSET = "UTF-8";
-
-  private static final Logger log = LoggerFactory.getLogger(ODataXmlSerializerImpl.class);
-
-  @Override
-  public SerializerResultImpl serviceDocument(final ServiceMetadata metadata, final String serviceRoot)
-      throws SerializerException {
-    throw new SerializerException("Service Document not implemented for XML format",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult metadataDocument(final ServiceMetadata serviceMetadata) throws SerializerException {
-    CircleStreamBuffer buffer;
-    XMLStreamWriter xmlStreamWriter = null;
-
-    try {
-      buffer = new CircleStreamBuffer();
-      xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET);
-      MetadataDocumentXmlSerializer serializer = new MetadataDocumentXmlSerializer(serviceMetadata);
-      serializer.writeMetadataDocument(xmlStreamWriter);
-      xmlStreamWriter.flush();
-      xmlStreamWriter.close();
-
-      return SerializerResultImpl.with().content(buffer.getInputStream()).build();
-    } catch (final XMLStreamException e) {
-      log.error(e.getMessage(), e);
-      throw new SerializerException("An I/O exception occurred.", e,
-          SerializerException.MessageKeys.IO_EXCEPTION);
-    } finally {
-      if (xmlStreamWriter != null) {
-        try {
-          xmlStreamWriter.close();
-        } catch (XMLStreamException e) {
-          throw new SerializerException("An I/O exception occurred.", e,
-              SerializerException.MessageKeys.IO_EXCEPTION);
-        }
-      }
-    }
-  }
-
-  @Override
-  public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType,
-      final Entity entity, final EntitySerializerOptions options) throws SerializerException {
-    throw new SerializerException("Entity serialization not implemented for XML format",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult entityCollection(final ServiceMetadata metadata,
-      final EdmEntityType entityType, final EntityCollection entitySet,
-      final EntityCollectionSerializerOptions options) throws SerializerException {
-    throw new SerializerException("Entityset serialization not implemented for XML format",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult error(final ODataServerError error) throws SerializerException {
-    throw new SerializerException("error serialization not implemented for XML format",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult primitive(final ServiceMetadata metadata, final EdmPrimitiveType type,
-      final Property property, final PrimitiveSerializerOptions options) throws SerializerException {
-    throw new SerializerException("Serialization not implemented for XML format.",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult complex(final ServiceMetadata metadata, final EdmComplexType type,
-      final Property property, final ComplexSerializerOptions options) throws SerializerException {
-    throw new SerializerException("Serialization not implemented for XML format.",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult primitiveCollection(final ServiceMetadata metadata, final EdmPrimitiveType type,
-      final Property property, final PrimitiveSerializerOptions options) throws SerializerException {
-    throw new SerializerException("Serialization not implemented for XML format.",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult complexCollection(final ServiceMetadata metadata, final EdmComplexType type,
-      final Property property, final ComplexSerializerOptions options) throws SerializerException {
-    throw new SerializerException("Serialization not implemented for XML format.",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult reference(final ServiceMetadata metadata, final EdmEntitySet edmEntotySet, 
-      final Entity entity, final ReferenceSerializerOptions options) throws SerializerException {
-    throw new SerializerException("Serialization not implemented for XML format.",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-
-  @Override
-  public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, 
-      final EntityCollection entityCollection, final ReferenceCollectionSerializerOptions optionsL) 
-          throws SerializerException {
-    throw new SerializerException("Serialization not implemented for XML format.",
-        SerializerException.MessageKeys.NOT_IMPLEMENTED);
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializer.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializer.java
new file mode 100644
index 0000000..ed60f36
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializer.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.xml;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmFunctionImport;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.edmx.EdmxReference;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+
+public class ServiceDocumentXmlSerializer {
+  public static final String KIND = "kind";
+
+  public static final String FUNCTION_IMPORT = "FunctionImport";
+  public static final String SINGLETON = "Singleton";
+  public static final String SERVICE_DOCUMENT = "ServiceDocument";
+  
+  private static final String APP = "app";
+  private static final String NS_APP = "http://www.w3.org/2007/app";
+  private static final String ATOM = "atom";
+  private static final String NS_ATOM = "http://www.w3.org/2005/Atom";
+  private static final String METADATA = "metadata";
+  private static final String NS_METADATA = "http://docs.oasis-open.org/odata/ns/metadata";
+
+  private final ServiceMetadata metadata;
+  private final String serviceRoot;
+
+  public ServiceDocumentXmlSerializer(final ServiceMetadata metadata, final String serviceRoot) {
+    this.metadata = metadata;
+    this.serviceRoot = serviceRoot;
+  }
+
+  public void writeServiceDocument(final XMLStreamWriter writer) throws XMLStreamException {
+    final String metadataUri =
+        (serviceRoot == null ? "" : serviceRoot.endsWith("/") ? serviceRoot : (serviceRoot + "/"))
+        + Constants.METADATA;
+    
+    writer.writeStartDocument(ODataSerializer.DEFAULT_CHARSET, "1.0");
+    writer.writeStartElement(APP, "service", NS_APP);
+    writer.writeNamespace(ATOM, NS_ATOM);
+    writer.writeNamespace(APP, NS_APP);
+    writer.writeNamespace(METADATA, NS_METADATA);
+    writer.writeAttribute(METADATA, NS_METADATA, "context", metadataUri);
+    
+    if (metadata != null
+        && metadata.getServiceMetadataETagSupport() != null
+        && metadata.getServiceMetadataETagSupport().getMetadataETag() != null) {
+      writer.writeAttribute(METADATA, NS_METADATA, "metadata-etag",
+          metadata.getServiceMetadataETagSupport().getMetadataETag());
+    }
+
+    writer.writeStartElement(APP, "workspace", NS_APP);
+    
+    final Edm edm = metadata.getEdm();
+
+    writer.writeStartElement(ATOM, "title", NS_APP);
+    writer.writeCharacters(edm.getEntityContainer(null).getFullQualifiedName().getFullQualifiedNameAsString());
+    writer.writeEndElement();
+    
+    writeEntitySets(writer, edm);
+    writeFunctionImports(writer, edm);
+    writeSingletons(writer, edm);
+    writeServiceDocuments(writer);
+    writer.writeEndElement(); // end workspace
+    writer.writeEndElement(); // end service   
+  }
+
+  private void writeServiceDocuments(XMLStreamWriter writer) throws XMLStreamException {
+    
+    for (EdmxReference reference : this.metadata.getReferences()) {
+      writer.writeStartElement(METADATA , "service-document", NS_METADATA);
+      writer.writeAttribute("href", reference.getUri().toASCIIString());
+      writer.writeStartElement(ATOM, "title", NS_ATOM);
+      writer.writeCharacters(reference.getUri().toASCIIString());
+      writer.writeEndElement();
+      writer.writeEndElement();
+    }
+  }
+
+  private void writeEntitySets(final XMLStreamWriter writer, final Edm edm) throws XMLStreamException {
+    EdmEntityContainer container = edm.getEntityContainer(null);
+    for (EdmEntitySet edmEntitySet : container.getEntitySets()) {
+      if (edmEntitySet.isIncludeInServiceDocument()) {
+        writer.writeStartElement(APP, "collection", NS_APP);
+        writer.writeAttribute("href", edmEntitySet.getName());
+        writer.writeStartElement(ATOM, "title", NS_ATOM);
+        writer.writeCharacters(edmEntitySet.getName());
+        writer.writeEndElement();
+        writer.writeEndElement();
+      }
+    }
+  }
+
+  private void writeFunctionImports(final XMLStreamWriter writer, final Edm edm) throws XMLStreamException {
+    EdmEntityContainer container = edm.getEntityContainer(null);
+
+    for (EdmFunctionImport edmFunctionImport : container.getFunctionImports()) {
+      if (edmFunctionImport.isIncludeInServiceDocument()) {
+        writer.writeStartElement(METADATA, "function-import", NS_METADATA);
+        writer.writeAttribute("href", edmFunctionImport.getName());
+        writer.writeStartElement(ATOM, "title", NS_ATOM);
+        writer.writeCharacters(edmFunctionImport.getName());
+        writer.writeEndElement();
+        writer.writeEndElement();        
+      }
+    }
+  }
+
+  private void writeSingletons(final XMLStreamWriter writer, final Edm edm) throws XMLStreamException {
+    EdmEntityContainer container = edm.getEntityContainer(null);
+    for (EdmSingleton edmSingleton : container.getSingletons()) {
+      writer.writeStartElement(METADATA, "singleton", NS_METADATA);
+      writer.writeAttribute("href", edmSingleton.getName());
+      writer.writeStartElement(ATOM, "title", NS_ATOM);
+      writer.writeCharacters( edmSingleton.getName());
+      writer.writeEndElement();
+      writer.writeEndElement();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
index d4af42a..0d17a5b 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
@@ -78,6 +78,8 @@ public class ContentNegotiatorTest {
       { ACCEPT_CASE_MIN_IEEE754,      null,             ACCEPT_CASE_MIN_IEEE754,        null                  },
       { ACCEPT_CASE_MIN_UTF8_IEEE754, null,             ACCEPT_CASE_MIN_UTF8_IEEE754,   null                  },
       { ACCEPT_CASE_MIN_IEEE754,      ACCEPT_CASE_MIN_IEEE754, ACCEPT_CASE_MIN ,        null                  },
+      { ACCEPT_CASE_XML,              "xml",            null,                           null                  },
+      { ACCEPT_CASE_XML,              null,             ACCEPT_CASE_XML,                null                  }      
   };
 
   String[][] casesMetadata = {
@@ -99,7 +101,6 @@ public class ContentNegotiatorTest {
       { null,                   "a/a",            null,                  "b/b"            },
       { null,                   "a/a;x=y",        null,                  "a/a;v=w"        },
       { null,                   null,             "a/a;x=y",             "a/a;v=w"        },
-      { null,                   "atom",           null,                  null             }, // not yet supported
       { null,                   null,             ACCEPT_CASE_FULL,      null             }, // not yet supported
       { null,                   "a/b;charset=ISO-8859-1", null,          "a/b"            },
       { null,                   null,             "a/b;charset=ISO-8859-1", "a/b"         },

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java
index 87a6554..3954a5a 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataImplTest.java
@@ -50,8 +50,7 @@ public class ODataImplTest {
     assertNotNull(odata.createDeserializer(ContentType.APPLICATION_JSON));
   }
 
-  @Test(expected = DeserializerException.class)
   public void xmlDeserializer() throws DeserializerException {
-    odata.createDeserializer(ContentType.APPLICATION_XML);
+    assertNotNull(odata.createDeserializer(ContentType.APPLICATION_XML));
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/MetadataDocumentXmlSerializerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/MetadataDocumentXmlSerializerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/MetadataDocumentXmlSerializerTest.java
index 4423308..9e9a04d 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/MetadataDocumentXmlSerializerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/MetadataDocumentXmlSerializerTest.java
@@ -45,6 +45,7 @@ import org.apache.olingo.commons.api.edm.provider.CsdlAliasInfo;
 import org.apache.olingo.commons.api.edm.provider.CsdlComplexType;
 import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider;
 import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer;
+import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainerInfo;
 import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet;
 import org.apache.olingo.commons.api.edm.provider.CsdlEntityType;
 import org.apache.olingo.commons.api.edm.provider.CsdlEnumMember;
@@ -73,7 +74,7 @@ import org.junit.Test;
 public class MetadataDocumentXmlSerializerTest {
 
   private static ODataSerializer serializer;
-
+  
   @BeforeClass
   public static void init() throws SerializerException {
     serializer = OData.newInstance().createSerializer(ContentType.APPLICATION_XML);
@@ -263,7 +264,7 @@ public class MetadataDocumentXmlSerializerTest {
         + "</ComplexType>"));
   }
 
-  private class LocalProvider extends CsdlAbstractEdmProvider {
+  static class LocalProvider extends CsdlAbstractEdmProvider {
     private final static String nameSpace = "namespace";
 
     private final FullQualifiedName nameETAbstract = new FullQualifiedName(nameSpace, "ETAbstract");
@@ -469,6 +470,15 @@ public class MetadataDocumentXmlSerializerTest {
     }
 
     @Override
+    public CsdlEntityContainerInfo getEntityContainerInfo(final FullQualifiedName entityContainerName)
+        throws ODataException {
+      if (entityContainerName == null) {
+        return new CsdlEntityContainerInfo().setContainerName(new FullQualifiedName("org.olingo", "container"));
+      }
+      return null;
+    }
+    
+    @Override
     public CsdlEntityContainer getEntityContainer() throws ODataException {
       CsdlEntityContainer container = new CsdlEntityContainer();
       container.setName("container");

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServerErrorXmlSerializerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServerErrorXmlSerializerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServerErrorXmlSerializerTest.java
new file mode 100644
index 0000000..6968b2b
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServerErrorXmlSerializerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.xml;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.olingo.commons.api.ODataErrorDetail;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataServerError;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ServerErrorXmlSerializerTest {
+
+  ODataSerializer ser;
+
+  @Before
+  public void before() throws Exception {
+    ser = OData.newInstance().createSerializer(ContentType.APPLICATION_XML);
+  }
+
+  @Test
+  public void basicODataErrorWithCode() throws Exception {
+    ODataServerError error = new ODataServerError();
+    error.setCode("Code").setMessage("ErrorMessage");
+    InputStream stream = ser.error(error).getContent();
+    String jsonString = IOUtils.toString(stream);
+    assertEquals("<?xml version='1.0' encoding='UTF-8'?>"
+        + "<error xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">"
+        + "<code>0</code>"
+        + "<message>ErrorMessage</message>"
+        + "</error>", 
+        jsonString);
+  }
+
+  @Test(expected = SerializerException.class)
+  public void nullErrorResultsInException() throws Exception {
+    ser.error(null);
+  }
+
+  @Test
+  public void singleDetailNothingSet() throws Exception {
+    ODataErrorDetail detail = new ODataErrorDetail();
+    detail.setCode("detail code");
+    detail.setMessage("detail message");
+    
+    List<ODataErrorDetail> details = new ArrayList<ODataErrorDetail>();
+    details.add(detail);
+    
+    ODataServerError error = new ODataServerError().setDetails(details);
+    error.setCode("code");
+    error.setMessage("err message");
+    error.setTarget("target");
+    
+    InputStream stream = ser.error(error).getContent();
+    String jsonString = IOUtils.toString(stream);
+    assertEquals("<?xml version='1.0' encoding='UTF-8'?>"
+        + "<error xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">"
+        + "<code>0</code>"
+        + "<message>err message</message>"
+        + "<target>target</target>"
+        + "<details>"
+        + "<code>detail code</code>"
+        + "<message>detail message</message>"
+        + "</details>"
+        + "</error>",
+        jsonString);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializerTest.java
new file mode 100644
index 0000000..04bf11d
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/xml/ServiceDocumentXmlSerializerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.xml;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmFunctionImport;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.edmx.EdmxReference;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.ServiceMetadataImpl;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class ServiceDocumentXmlSerializerTest {
+  private static ODataSerializer serializer;
+
+  @BeforeClass
+  public static void init() throws SerializerException {
+    serializer = OData.newInstance().createSerializer(ContentType.APPLICATION_ATOM_XML);
+  }
+  
+  @Test
+  public void writeServiceWithEmptyMockedEdm() throws Exception {
+    final Edm edm = mock(Edm.class);
+    EdmEntityContainer container = mock(EdmEntityContainer.class);
+    Mockito.stub(container.getFullQualifiedName()).toReturn(new FullQualifiedName("service.test"));
+    when(container.getEntitySets()).thenReturn(new ArrayList<EdmEntitySet>());
+    when(container.getFunctionImports()).thenReturn(new ArrayList<EdmFunctionImport>());
+    when(container.getSingletons()).thenReturn(new ArrayList<EdmSingleton>());
+    when(edm.getEntityContainer(null)).thenReturn(container);
+    ServiceMetadata metadata = mock(ServiceMetadata.class);
+    when(metadata.getEdm()).thenReturn(edm);
+
+    assertEquals("<?xml version='1.0' encoding='UTF-8'?>"
+        + "<app:service xmlns:atom=\"http://www.w3.org/2005/Atom\" "
+        + "xmlns:app=\"http://www.w3.org/2007/app\" "
+        + "xmlns:metadata=\"http://docs.oasis-open.org/odata/ns/metadata\" "
+        + "metadata:context=\"http://host/svc/$metadata\">"
+        + "<app:workspace><atom:title>service.test</atom:title></app:workspace>"
+        + "</app:service>",
+        IOUtils.toString(serializer.serviceDocument(metadata, "http://host/svc").getContent()));
+  }
+
+  @Test
+  public void writeServiceDocument() throws Exception {
+    CsdlEdmProvider provider = new MetadataDocumentXmlSerializerTest.LocalProvider();
+    ServiceMetadata serviceMetadata = new ServiceMetadataImpl(provider, 
+        Collections.<EdmxReference> emptyList(), null);
+    InputStream metadataStream = serializer.serviceDocument(serviceMetadata, "http://host/svc").getContent();
+    String metadata = IOUtils.toString(metadataStream);
+    assertEquals("<?xml version='1.0' encoding='UTF-8'?>"
+        + "<app:service xmlns:atom=\"http://www.w3.org/2005/Atom\" "
+        + "xmlns:app=\"http://www.w3.org/2007/app\" "
+        + "xmlns:metadata=\"http://docs.oasis-open.org/odata/ns/metadata\" "
+        + "metadata:context=\"http://host/svc/$metadata\">"
+        + "<app:workspace>"
+        + "<atom:title>org.olingo.container</atom:title>"
+        +   "<app:collection href=\"ESAllPrim\">"
+        +     "<atom:title>ESAllPrim</atom:title>"
+        +   "</app:collection>"
+        +   "<metadata:function-import href=\"FINRTInt16\">"
+        +     "<atom:title>FINRTInt16</atom:title>"
+        +   "</metadata:function-import>"
+        +   "<metadata:singleton href=\"SI\">"
+        +     "<atom:title>SI</atom:title>"
+        +   "</metadata:singleton>"
+        + "</app:workspace>"
+        + "</app:service>",
+        metadata);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
index 0b5922c..51a2c34 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
@@ -75,7 +75,7 @@ public class TechnicalServlet extends HttpServlet {
       HttpSession session = request.getSession(true);
       DataProvider dataProvider = (DataProvider) session.getAttribute(DataProvider.class.getName());
       if (dataProvider == null) {
-        dataProvider = new DataProvider();
+        dataProvider = new DataProvider(serviceMetadata.getEdm());
         session.setAttribute(DataProvider.class.getName(), dataProvider);
         LOG.info("Created new data provider.");
       }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/ActionData.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/ActionData.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/ActionData.java
index 6d7a41f..65a56c7 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/ActionData.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/ActionData.java
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -36,7 +37,10 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.edmx.EdmxReference;
 import org.apache.olingo.server.tecsvc.data.DataProvider.DataProviderException;
+import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
 
 public class ActionData {
 
@@ -151,6 +155,9 @@ public class ActionData {
 
   protected static EntityActionResult entityAction(final String name, final Map<String, Parameter> parameters)
       throws DataProviderException {
+    final ServiceMetadata metadata = OData.newInstance().createServiceMetadata(new EdmTechProvider(), 
+        Collections.<EdmxReference> emptyList(),null);
+    
     if ("UARTETTwoKeyTwoPrimParam".equals(name)) {
       Parameter parameter = parameters.get("ParameterInt16");
       Short number;
@@ -160,7 +167,7 @@ public class ActionData {
         number = (short) 0;
       }
 
-      EntityCollection entityCollection = new DataCreator().getData().get("ESTwoKeyTwoPrim");
+      EntityCollection entityCollection = new DataCreator(metadata.getEdm()).getData().get("ESTwoKeyTwoPrim");
       for (Entity entity : entityCollection.getEntities()) {
         Object asPrimitive = entity.getProperty("PropertyInt16").asPrimitive();
         if (number.equals(asPrimitive)) {
@@ -171,7 +178,7 @@ public class ActionData {
       throw new DataProviderException("Entity not found with key: " + number, HttpStatusCode.NOT_FOUND);
     } else if ("UARTETAllPrimParam".equals(name)) {
       Parameter paramDate = parameters.get("ParameterDate");
-      EntityCollection entityCollection = new DataCreator().getData().get("ESAllPrim");
+      EntityCollection entityCollection = new DataCreator(metadata.getEdm()).getData().get("ESAllPrim");
       if (paramDate != null) {
         Calendar date = (Calendar) paramDate.asPrimitive();
         boolean freeKey;