You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by fm...@apache.org on 2014/04/10 16:33:29 UTC

[50/51] [partial] [OLINGO-175] using commons (de-)serializers to add some capabilities to fit

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java b/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java
new file mode 100644
index 0000000..84ad043
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java
@@ -0,0 +1,354 @@
+/*
+ * 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.fit.metadata;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import org.apache.commons.io.IOUtils;
+import org.codehaus.plexus.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Metadata extends AbstractMetadataElement {
+
+  /**
+   * Logger.
+   */
+  protected static final Logger LOG = LoggerFactory.getLogger(Metadata.class);
+
+  private final Map<String, Schema> schemas;
+
+  public Metadata(final InputStream is) {
+    this.schemas = new HashMap<String, Schema>();
+
+    try {
+      final XMLInputFactory ifactory = XMLInputFactory.newInstance();
+      ifactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
+      final XMLEventReader reader = ifactory.createXMLEventReader(is, "utf-8");
+
+      try {
+        while (reader.hasNext()) {
+          XMLEvent event = reader.nextEvent();
+
+          if (event.isStartElement() && event.asStartElement().getName().equals(new QName("Schema"))) {
+            final Schema schema = getSchema(event.asStartElement(), reader);
+            schemas.put(schema.getNamespace(), schema);
+          }
+        }
+
+      } catch (Exception ignore) {
+        // ignore
+      } finally {
+        reader.close();
+        IOUtils.closeQuietly(is);
+      }
+    } catch (Exception e) {
+      LOG.error("Error parsing metadata", e);
+    }
+
+    for (Map.Entry<String, Schema> schemaEntry : schemas.entrySet()) {
+      for (EntityType entityType : schemaEntry.getValue().getEntityTypes()) {
+        for (NavigationProperty property : entityType.getNavigationProperties()) {
+          if (StringUtils.isNotBlank(property.getReleationship())) {
+            // V3 ...
+            final Association association = schemaEntry.getValue().getAssociation(
+                    property.getReleationship().replaceAll(schemaEntry.getKey() + "\\.", ""));
+            final Association.Role role = association.getRole(property.getToRole());
+            property.setFeed(role.getMultiplicity().equals("*"));
+            property.setType(property.isFeed() ? "Collection(" + role.getType() + ")" : role.getType());
+
+            // let me assume that it will be just a single container
+            final AssociationSet associationSet = schemaEntry.getValue().getContainers().iterator().next().
+                    getAssociationSet(property.getReleationship());
+
+            final AssociationSet.Role associationSetRole = associationSet.getRole(property.getToRole());
+            property.setTarget(associationSetRole.getEntitySet());
+          } else {
+            // V4 ...
+            property.setFeed(property.getType().startsWith("Collection("));
+
+            final Collection<EntitySet> entitySets = schemaEntry.getValue().getContainers().iterator().next().
+                    getEntitySets(schemaEntry.getKey(), entityType.getName());
+
+            final Iterator<EntitySet> iter = entitySets.iterator();
+            boolean found = false;
+
+            while (!found && iter.hasNext()) {
+              final EntitySet entitySet = iter.next();
+              final String target = entitySet.getTarget(property.getName());
+              if (StringUtils.isNotBlank(target)) {
+                property.setTarget(entitySet.getTarget(property.getName()));
+                found = true;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public EntitySet getEntitySet(final String name) {
+    for (Schema schema : getSchemas()) {
+      for (Container container : schema.getContainers()) {
+        final EntitySet entitySet = container.getEntitySet(name);
+        if (entitySet != null) {
+          return entitySet;
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public EntityType getEntityType(final String fqn) {
+    int lastDotIndex = fqn.lastIndexOf('.');
+    final String ns = fqn.substring(0, lastDotIndex).replaceAll("^#", "");
+    final String name = fqn.substring(lastDotIndex + 1);
+    return getSchema(ns).getEntityType(name);
+  }
+
+  public Map<String, NavigationProperty> getNavigationProperties(final String entitySetName) {
+
+    for (Schema schema : getSchemas()) {
+      for (Container container : schema.getContainers()) {
+        final EntitySet entitySet = container.getEntitySet(entitySetName);
+        if (entitySet != null) {
+          final String entityTypeFQN = entitySet.getType();
+          final int lastDotIndex = entityTypeFQN.lastIndexOf('.');
+          final String entityTypeNS = entityTypeFQN.substring(0, lastDotIndex);
+          final String entityTypeName = entityTypeFQN.substring(lastDotIndex + 1);
+
+          final EntityType entityType = getSchema(entityTypeNS).getEntityType(entityTypeName);
+
+          return entityType.getNavigationPropertyMap();
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public Collection<Schema> getSchemas() {
+    return schemas.values();
+  }
+
+  public Schema getSchema(final String namespace) {
+    return schemas.get(namespace);
+  }
+
+  public Metadata addSchema(final String namespace, final Schema schema) {
+    schemas.put(namespace, schema);
+    return this;
+  }
+
+  private Schema getSchema(final StartElement start, final XMLEventReader reader) throws XMLStreamException {
+    final Schema schema = new Schema(start.getAttributeByName(new QName("Namespace")).getValue());
+
+    boolean completed = false;
+
+    while (!completed && reader.hasNext()) {
+      XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement() && event.asStartElement().getName().equals(new QName("EntityType"))
+              || event.isStartElement() && event.asStartElement().getName().equals(new QName("ComplexType"))) {
+        final EntityType entityType = getEntityType(event.asStartElement(), reader);
+        schema.addEntityType(entityType.getName(), entityType);
+      } else if (event.isStartElement() && event.asStartElement().getName().equals(new QName("EntityContainer"))) {
+        final org.apache.olingo.fit.metadata.Container container = getContainer(event.asStartElement(), reader);
+        schema.addContainer(container.getName(), container);
+      } else if (event.isStartElement() && event.asStartElement().getName().equals(new QName("Association"))) {
+        // just for V3
+        final Association association = getAssociation(event.asStartElement(), reader);
+        schema.addAssociation(association.getName(), association);
+      } else if (event.isEndElement() && event.asEndElement().getName().equals(start.getName())) {
+        completed = true;
+      }
+    }
+
+    return schema;
+  }
+
+  private org.apache.olingo.fit.metadata.Container getContainer(
+          final StartElement start, final XMLEventReader reader) throws XMLStreamException {
+    final org.apache.olingo.fit.metadata.Container container =
+            new org.apache.olingo.fit.metadata.Container(start.getAttributeByName(new QName("Name")).getValue());
+
+    boolean completed = false;
+
+    while (!completed && reader.hasNext()) {
+      XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement()
+              && (event.asStartElement().getName().equals(new QName("EntitySet"))
+              || event.asStartElement().getName().equals(new QName("Singleton")))) {
+        final EntitySet entitySet = getEntitySet(event.asStartElement(), reader);
+        container.addEntitySet(entitySet.getName(), entitySet);
+      } else if (event.isStartElement() && event.asStartElement().getName().equals(new QName("AssociationSet"))) {
+        // just for V3
+        final AssociationSet associationSet = getAssociationSet(event.asStartElement(), reader);
+        container.addAssociationSet(associationSet.getAssociation(), associationSet);
+      } else if (event.isEndElement() && event.asEndElement().getName().equals(start.getName())) {
+        completed = true;
+      }
+    }
+
+    return container;
+  }
+
+  private Association getAssociation(
+          final StartElement start, final XMLEventReader reader) throws XMLStreamException {
+    final Association association = new Association(start.getAttributeByName(new QName("Name")).getValue());
+
+    boolean completed = false;
+
+    while (!completed && reader.hasNext()) {
+      XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement() && event.asStartElement().getName().equals(new QName("End"))) {
+        final String role = event.asStartElement().getAttributeByName(new QName("Role")).getValue();
+        final String type = event.asStartElement().getAttributeByName(new QName("Type")).getValue();
+        final String multiplicity = event.asStartElement().getAttributeByName(new QName("Multiplicity")).getValue();
+        association.addRole(role, type, multiplicity);
+      } else if (event.isEndElement() && event.asEndElement().getName().equals(start.getName())) {
+        completed = true;
+      }
+    }
+
+    return association;
+  }
+
+  private AssociationSet getAssociationSet(
+          final StartElement start, final XMLEventReader reader) throws XMLStreamException {
+    final AssociationSet associationSet = new AssociationSet(
+            start.getAttributeByName(new QName("Name")).getValue(),
+            start.getAttributeByName(new QName("Association")).getValue());
+
+    boolean completed = false;
+
+    while (!completed && reader.hasNext()) {
+      XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement() && event.asStartElement().getName().equals(new QName("End"))) {
+        final String role = event.asStartElement().getAttributeByName(new QName("Role")).getValue();
+        final String entitySet = event.asStartElement().getAttributeByName(new QName("EntitySet")).getValue();
+        associationSet.addRole(role, entitySet);
+      } else if (event.isEndElement() && event.asEndElement().getName().equals(start.getName())) {
+        completed = true;
+      }
+    }
+
+    return associationSet;
+  }
+
+  private EntityType getEntityType(final StartElement start, final XMLEventReader reader) throws XMLStreamException {
+    final EntityType entityType = new EntityType(start.getAttributeByName(new QName("Name")).getValue());
+
+    boolean completed = false;
+
+    while (!completed && reader.hasNext()) {
+      XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement() && event.asStartElement().getName().equals(new QName("Property"))) {
+        final org.apache.olingo.fit.metadata.Property property = getProperty(event.asStartElement());
+        entityType.addProperty(property.getName(), property);
+      } else if (event.isStartElement() && event.asStartElement().getName().equals(new QName("NavigationProperty"))) {
+        final NavigationProperty property = getNavigationProperty(event.asStartElement());
+        entityType.addNavigationProperty(property.getName(), property);
+      } else if (event.isEndElement() && event.asEndElement().getName().equals(start.getName())) {
+        completed = true;
+      }
+    }
+
+    return entityType;
+  }
+
+  private org.apache.olingo.fit.metadata.Property getProperty(final StartElement start) throws XMLStreamException {
+    final org.apache.olingo.fit.metadata.Property property =
+            new org.apache.olingo.fit.metadata.Property(start.getAttributeByName(new QName("Name")).getValue());
+
+    final Attribute type = start.getAttributeByName(new QName("Type"));
+    property.setType(type == null ? "Edm.String" : type.getValue());
+
+    final Attribute nullable = start.getAttributeByName(new QName("Nullable"));
+    property.setNullable(nullable == null || !"false".equals(nullable.getValue()));
+
+    return property;
+  }
+
+  private NavigationProperty getNavigationProperty(final StartElement start) throws XMLStreamException {
+    final NavigationProperty property = new NavigationProperty(start.getAttributeByName(new QName("Name")).getValue());
+
+    final Attribute type = start.getAttributeByName(new QName("Type"));
+    if (type != null) {
+      property.setType(type.getValue());
+    }
+
+    final Attribute relationship = start.getAttributeByName(new QName("Relationship"));
+    if (relationship != null) {
+      property.setReleationship(relationship.getValue());
+    }
+
+    final Attribute toRole = start.getAttributeByName(new QName("ToRole"));
+    if (toRole != null) {
+      property.setToRole(toRole.getValue());
+    }
+
+    return property;
+  }
+
+  private EntitySet getEntitySet(final StartElement start, final XMLEventReader reader) throws XMLStreamException {
+    final EntitySet entitySet = "Singleton".equals(start.getName().getLocalPart())
+            ? new EntitySet(start.getAttributeByName(new QName("Name")).getValue(), true)
+            : new EntitySet(start.getAttributeByName(new QName("Name")).getValue());
+
+    Attribute type = start.getAttributeByName(new QName("EntityType"));
+    if (type == null) {
+      type = start.getAttributeByName(new QName("Type"));
+      entitySet.setType(type == null ? null : type.getValue());
+    } else {
+      entitySet.setType(type.getValue());
+    }
+
+    boolean completed = false;
+
+    while (!completed && reader.hasNext()) {
+      XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement() && event.asStartElement().getName().equals(new QName("NavigationPropertyBinding"))) {
+        final String path = event.asStartElement().getAttributeByName(new QName("Path")).getValue();
+        final String target = event.asStartElement().getAttributeByName(new QName("Target")).getValue();
+        entitySet.addBinding(path, target);
+      } else if (event.isEndElement() && event.asEndElement().getName().equals(start.getName())) {
+        completed = true;
+      }
+    }
+
+    return entitySet;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/metadata/NavigationProperty.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/metadata/NavigationProperty.java b/fit/src/main/java/org/apache/olingo/fit/metadata/NavigationProperty.java
new file mode 100644
index 0000000..eb3a74f
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/metadata/NavigationProperty.java
@@ -0,0 +1,86 @@
+/*
+ * 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.fit.metadata;
+
+public class NavigationProperty extends AbstractMetadataElement {
+
+  private final String name;
+
+  // -----------------------
+  // just for v3
+  // -----------------------
+  private String releationship;
+
+  private String toRole;
+  // -----------------------
+
+  private String type;
+
+  private String target;
+
+  private boolean feed;
+
+  public NavigationProperty(final String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getReleationship() {
+    return releationship;
+  }
+
+  public void setReleationship(String releationship) {
+    this.releationship = releationship;
+  }
+
+  public String getToRole() {
+    return toRole;
+  }
+
+  public void setToRole(String toRole) {
+    this.toRole = toRole;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getTarget() {
+    return target;
+  }
+
+  public void setTarget(String target) {
+    this.target = target;
+  }
+
+  public boolean isFeed() {
+    return feed;
+  }
+
+  public void setFeed(boolean feed) {
+    this.feed = feed;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/metadata/Property.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/metadata/Property.java b/fit/src/main/java/org/apache/olingo/fit/metadata/Property.java
new file mode 100644
index 0000000..0ce180b
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/metadata/Property.java
@@ -0,0 +1,52 @@
+/*
+ * 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.fit.metadata;
+
+public class Property extends AbstractMetadataElement {
+
+  private final String name;
+
+  private String type;
+
+  private boolean nullable;
+
+  public Property(String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public boolean isNullable() {
+    return nullable;
+  }
+
+  public void setNullable(boolean nullable) {
+    this.nullable = nullable;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/metadata/Schema.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/metadata/Schema.java b/fit/src/main/java/org/apache/olingo/fit/metadata/Schema.java
new file mode 100644
index 0000000..04cf75a
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/metadata/Schema.java
@@ -0,0 +1,80 @@
+/*
+ * 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.fit.metadata;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Schema extends AbstractMetadataElement {
+
+  private final String namespace;
+
+  private final Map<String, Container> containers;
+
+  private final Map<String, Association> associations;
+
+  private final Map<String, EntityType> entityTypes;
+
+  public Schema(final String namespace) {
+    this.namespace = namespace;
+    entityTypes = new HashMap<String, EntityType>();
+    this.containers = new HashMap<String, Container>();
+    this.associations = new HashMap<String, Association>();
+  }
+
+  public String getNamespace() {
+    return namespace;
+  }
+
+  public Collection<EntityType> getEntityTypes() {
+    return entityTypes.values();
+  }
+
+  public EntityType getEntityType(final String name) {
+    return entityTypes.get(name);
+  }
+
+  public Schema addEntityType(final String name, final EntityType entityType) {
+    entityTypes.put(name, entityType);
+    return this;
+  }
+
+  public Collection<Container> getContainers() {
+    return containers.values();
+  }
+
+  public Container getContainer(final String name) {
+    return containers.get(name);
+  }
+
+  public Schema addContainer(final String name, final Container container) {
+    containers.put(name, container);
+    return this;
+  }
+
+  public Association getAssociation(final String name) {
+    return associations.get(name);
+  }
+
+  public Schema addAssociation(final String name, final Association association) {
+    associations.put(name, association);
+    return this;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/serializer/JsonEntryContainer.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/serializer/JsonEntryContainer.java b/fit/src/main/java/org/apache/olingo/fit/serializer/JsonEntryContainer.java
new file mode 100644
index 0000000..5ff4f6e
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/serializer/JsonEntryContainer.java
@@ -0,0 +1,35 @@
+/*
+ * 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.fit.serializer;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.net.URI;
+import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.core.data.JSONEntryDeserializer;
+import org.apache.olingo.commons.core.data.JSONEntrySerializer;
+
+@JsonDeserialize(using = JSONEntryDeserializer.class)
+@JsonSerialize(using = JSONEntrySerializer.class)
+public class JsonEntryContainer<T> extends Container<T> {
+
+  public JsonEntryContainer(final URI contextURL, final String metadataETag, final T object) {
+    super(contextURL, metadataETag, object);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/serializer/JsonFeedContainer.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/serializer/JsonFeedContainer.java b/fit/src/main/java/org/apache/olingo/fit/serializer/JsonFeedContainer.java
new file mode 100644
index 0000000..6af08dd
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/serializer/JsonFeedContainer.java
@@ -0,0 +1,35 @@
+/*
+ * 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.fit.serializer;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.net.URI;
+import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.core.data.JSONFeedDeserializer;
+import org.apache.olingo.commons.core.data.JSONFeedSerializer;
+
+@JsonDeserialize(using = JSONFeedDeserializer.class)
+@JsonSerialize(using = JSONFeedSerializer.class)
+public class JsonFeedContainer<T> extends Container<T> {
+
+  public JsonFeedContainer(final URI contextURL, final String metadataETag, final T object) {
+    super(contextURL, metadataETag, object);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java
index 6c6b7cc..1354a6f 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java
@@ -38,10 +38,13 @@ import java.util.Set;
 import javax.ws.rs.NotFoundException;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.fit.metadata.Metadata;
+import org.apache.olingo.fit.metadata.NavigationProperty;
 
 public abstract class AbstractJSONUtilities extends AbstractUtilities {
 
-  public AbstractJSONUtilities(final ODataVersion version) throws Exception {
+  public AbstractJSONUtilities(final ODataServiceVersion version) throws Exception {
     super(version);
   }
 
@@ -109,6 +112,9 @@ public abstract class AbstractJSONUtilities extends AbstractUtilities {
 
     final Iterator<Map.Entry<String, JsonNode>> fieldIter = srcNode.fields();
 
+    final Metadata metadata = Commons.getMetadata(version);
+    final Map<String, NavigationProperty> navigationProperties = metadata.getNavigationProperties(entitySetName);
+
     while (fieldIter.hasNext()) {
       final Map.Entry<String, JsonNode> field = fieldIter.next();
       if (field.getKey().endsWith(Constants.get(version, ConstantKey.JSON_NAVIGATION_BIND_SUFFIX))) {
@@ -125,7 +131,7 @@ public abstract class AbstractJSONUtilities extends AbstractUtilities {
         }
 
         links.addLinks(title, hrefs);
-      } else if (Commons.linkInfo.get(version).exists(entitySetName, field.getKey())) {
+      } else if (navigationProperties.containsKey(field.getKey())) {
         links.addInlines(field.getKey(), IOUtils.toInputStream(field.getValue().toString(), "UTf-8"));
       }
     }
@@ -170,7 +176,7 @@ public abstract class AbstractJSONUtilities extends AbstractUtilities {
 
     srcNode.set(
             Constants.get(version, ConstantKey.JSON_EDITLINK_NAME), new TextNode(
-                    Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + entitySetName + "(" + entityKey + ")"));
+            Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + entitySetName + "(" + entityKey + ")"));
 
     return IOUtils.toInputStream(srcNode.toString(), "UTf-8");
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
index 360a106..569a3b0 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
@@ -40,7 +40,10 @@ import javax.ws.rs.core.Response;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.vfs2.FileObject;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 import org.apache.olingo.fit.UnsupportedMediaTypeException;
+import org.apache.olingo.fit.metadata.Metadata;
+import org.apache.olingo.fit.metadata.NavigationProperty;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,13 +54,13 @@ public abstract class AbstractUtilities {
    */
   protected static final Logger LOG = LoggerFactory.getLogger(AbstractUtilities.class);
 
-  protected final ODataVersion version;
+  protected final ODataServiceVersion version;
 
   protected final FSManager fsManager;
 
   protected final static Pattern entityUriPattern = Pattern.compile(".*\\/.*\\(.*\\)");
 
-  public AbstractUtilities(final ODataVersion version) throws Exception {
+  public AbstractUtilities(final ODataServiceVersion version) throws Exception {
     this.version = version;
     this.fsManager = FSManager.instance(version);
   }
@@ -168,12 +171,15 @@ public abstract class AbstractUtilities {
     IOUtils.copy(is, bos);
     IOUtils.closeQuietly(is);
 
+    final Map<String, NavigationProperty> navigationProperties =
+            Commons.getMetadata(version).getNavigationProperties(entitySetName);
+
     // -----------------------------------------
     // 0. Retrieve navigation links to be mantained
     // -----------------------------------------
     Set<String> linksToBeMantained;
     try {
-      linksToBeMantained = Commons.linkInfo.get(version).getNavigationLinkNames(entitySetName);
+      linksToBeMantained = new HashSet<String>(navigationProperties.keySet());
     } catch (Exception e) {
       linksToBeMantained = Collections.<String>emptySet();
     }
@@ -195,7 +201,7 @@ public abstract class AbstractUtilities {
     // 1. Get default entry key and path (N.B. operation will consume/close the stream; use a copy instead)
     // -----------------------------------------
     final String entityKey = key == null ? getDefaultEntryKey(
-            entitySetName, new ByteArrayInputStream(bos.toByteArray()), getDefaultFormat()) : key;
+            entitySetName, new ByteArrayInputStream(bos.toByteArray())) : key;
 
     final String path = Commons.getEntityBasePath(entitySetName, entityKey);
     // -----------------------------------------
@@ -236,14 +242,13 @@ public abstract class AbstractUtilities {
 
     // -----------------------------------------
     // 4. Create links file and provided inlines
-    // -----------------------------------------
+    // -----------------------------------------  
     for (Map.Entry<String, List<String>> link : links.getLinks()) {
       putLinksInMemory(path, entitySetName, entityKey, link.getKey(), link.getValue());
     }
 
     for (Map.Entry<String, List<InputStream>> inlineEntry : links.getInlines()) {
-      final String inlineEntitySetName =
-              Commons.linkInfo.get(version).getTargetName(entitySetName, inlineEntry.getKey());
+      final String inlineEntitySetName = navigationProperties.get(inlineEntry.getKey()).getTarget();
 
       final List<String> hrefs = new ArrayList<String>();
 
@@ -253,7 +258,7 @@ public abstract class AbstractUtilities {
         IOUtils.closeQuietly(stream);
 
         final String inlineEntryKey = getDefaultEntryKey(
-                inlineEntitySetName, new ByteArrayInputStream(inlineBos.toByteArray()), getDefaultFormat());
+                inlineEntitySetName, new ByteArrayInputStream(inlineBos.toByteArray()));
 
         addOrReplaceEntity(
                 inlineEntryKey,
@@ -270,14 +275,14 @@ public abstract class AbstractUtilities {
     return fo.getContent().getInputStream();
   }
 
-  public InputStream addMediaEntity(
+  public void addMediaEntityValue(
           final String entitySetName,
+          final String entityKey,
           final InputStream is) throws Exception {
 
     // -----------------------------------------
     // 0. Get default entry key and path (N.B. operation will consume/close the stream; use a copy instead)
     // -----------------------------------------
-    final String entityKey = getDefaultEntryKey(entitySetName, null, getDefaultFormat());
     final String path = Commons.getEntityBasePath(entitySetName, entityKey);
     // -----------------------------------------
 
@@ -288,6 +293,17 @@ public abstract class AbstractUtilities {
             + Constants.get(version, ConstantKey.MEDIA_CONTENT_FILENAME), null));
     IOUtils.closeQuietly(is);
     // -----------------------------------------
+  }
+
+  public InputStream addMediaEntity(
+          final String entitySetName,
+          final InputStream is) throws Exception {
+
+    final String entityKey = getDefaultEntryKey(entitySetName, null);
+
+    addMediaEntityValue(entitySetName, entityKey, is);
+
+    final String path = Commons.getEntityBasePath(entitySetName, entityKey);
 
     // -----------------------------------------
     // 2. save entity as atom
@@ -334,7 +350,7 @@ public abstract class AbstractUtilities {
 
     fsManager.putInMemory(
             IOUtils.toInputStream(entity), fsManager.getAbsolutePath(path + Constants.get(version, ConstantKey.ENTITY),
-                    Accept.JSON_FULLMETA));
+            Accept.JSON_FULLMETA));
     // -----------------------------------------
 
     return readEntity(entitySetName, entityKey, getDefaultFormat()).getValue();
@@ -349,7 +365,10 @@ public abstract class AbstractUtilities {
 
     final HashSet<String> uris = new HashSet<String>();
 
-    if (Commons.linkInfo.get(version).isFeed(entitySetName, linkName)) {
+    final Metadata metadata = Commons.getMetadata(version);
+    final Map<String, NavigationProperty> navigationProperties = metadata.getNavigationProperties(entitySetName);
+
+    if (navigationProperties.get(linkName).isFeed()) {
       try {
         final Map.Entry<String, List<String>> currents = extractLinkURIs(entitySetName, entityKey, linkName);
         uris.addAll(currents.getValue());
@@ -383,8 +402,8 @@ public abstract class AbstractUtilities {
   public Response createResponse(
           final InputStream entity, final String etag, final Accept accept, final Response.Status status) {
     final Response.ResponseBuilder builder = Response.ok();
-    if (version == ODataVersion.v3) {
-      builder.header(Constants.get(version, ConstantKey.ODATA_SERVICE_VERSION), version.getVersion() + ";");
+    if (version.compareTo(ODataServiceVersion.V30) <= 0) {
+      builder.header(Constants.get(version, ConstantKey.ODATA_SERVICE_VERSION), version.toString() + ";");
     }
 
     if (StringUtils.isNotBlank(etag)) {
@@ -432,7 +451,7 @@ public abstract class AbstractUtilities {
     LOG.debug("Create fault response about .... ", e);
 
     final Response.ResponseBuilder builder = Response.serverError();
-    if (version == ODataVersion.v3) {
+    if (version.compareTo(ODataServiceVersion.V30) <= 0) {
       builder.header(Constants.get(version, ConstantKey.ODATA_SERVICE_VERSION), version + ";");
     }
 
@@ -441,7 +460,7 @@ public abstract class AbstractUtilities {
     if (accept.startsWith("application/json")) {
       ext = ".json";
       contentType = Accept.JSON;
-    } else if (accept.startsWith("application/xml") || version == ODataVersion.v3) {
+    } else if (accept.startsWith("application/xml") || version.compareTo(ODataServiceVersion.V30) <= 0) {
       ext = ".xml";
       contentType = Accept.XML;
     } else {
@@ -470,8 +489,8 @@ public abstract class AbstractUtilities {
     return builder.build();
   }
 
-  protected String getDefaultEntryKey(
-          final String entitySetName, final InputStream entity, final Accept accept) throws IOException {
+  public String getDefaultEntryKey(
+          final String entitySetName, final InputStream entity) throws IOException {
     try {
       String res;
 
@@ -649,7 +668,10 @@ public abstract class AbstractUtilities {
 
     final LinkInfo linkInfo = new LinkInfo(fsManager.readFile(basePath + linkName, accept));
     linkInfo.setEtag(Commons.getETag(basePath, version));
-    linkInfo.setFeed(Commons.linkInfo.get(version).isFeed(entitySetName, linkName));
+    final Metadata metadata = Commons.getMetadata(version);
+    final Map<String, NavigationProperty> navigationProperties = metadata.getNavigationProperties(entitySetName);
+
+    linkInfo.setFeed(navigationProperties.get(linkName.replaceAll("\\(.*\\)", "")).isFeed());
 
     return linkInfo;
   }
@@ -695,31 +717,38 @@ public abstract class AbstractUtilities {
   public InputStream expandEntity(
           final String entitySetName,
           final String entityId,
-          final InputStream entity,
           final String linkName)
           throws Exception {
 
     // --------------------------------
     // 0. Retrieve all 'linkName' navigation link uris (NotFoundException if missing) 
     // --------------------------------
-    final LinkInfo linkInfo = readLinks(entitySetName, entityId, linkName, Accept.XML);
     final Map.Entry<String, List<String>> links = extractLinkURIs(entitySetName, entityId, linkName);
     // --------------------------------
 
     // --------------------------------
     // 1. Retrieve expanded object (entry or feed)
     // --------------------------------
-    final InputStream expanded = readEntities(
+    final Metadata metadata = Commons.getMetadata(version);
+    final Map<String, NavigationProperty> navigationProperties = metadata.getNavigationProperties(entitySetName);
+
+    return readEntities(
             links.getValue(),
             linkName,
             links.getKey(),
-            linkInfo.isFeed());
-    // --------------------------------
+            navigationProperties.get(linkName).isFeed());
+  }
 
+  public InputStream expandEntity(
+          final String entitySetName,
+          final String entityId,
+          final InputStream entity,
+          final String linkName)
+          throws Exception {
     // --------------------------------
     // 2. Retrieve expanded object (entry or feed)
     // --------------------------------
-    return replaceLink(entity, linkName, expanded);
+    return replaceLink(entity, linkName, expandEntity(entitySetName, entityId, linkName));
     // --------------------------------
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java
index 831ae42..a47ca02 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java
@@ -52,6 +52,9 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystemException;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.fit.metadata.Metadata;
+import org.apache.olingo.fit.metadata.NavigationProperty;
 
 public abstract class AbstractXMLUtilities extends AbstractUtilities {
 
@@ -59,12 +62,10 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
 
   protected static XMLOutputFactory ofactory = null;
 
-  public AbstractXMLUtilities(final ODataVersion version) throws Exception {
+  public AbstractXMLUtilities(final ODataServiceVersion version) throws Exception {
     super(version);
   }
 
-  public abstract void retrieveLinkInfoFromMetadata() throws Exception;
-
   @Override
   protected Accept getDefaultFormat() {
     return Accept.ATOM;
@@ -146,6 +147,9 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
 
     writer.add(entry.getValue().getStart());
 
+    final Metadata metadata = Commons.getMetadata(version);
+    final Map<String, NavigationProperty> navigationProperties = metadata.getNavigationProperties(entitySetName);
+
     // add for links
     for (String link : links) {
       final Set<Attribute> attributes = new HashSet<Attribute>();
@@ -155,7 +159,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
       attributes.add(eventFactory.createAttribute(new QName("rel"),
               Constants.get(version, ConstantKey.ATOM_LINK_REL) + link));
       attributes.add(eventFactory.createAttribute(new QName("type"),
-              Commons.linkInfo.get(version).isFeed(entitySetName, link)
+              navigationProperties.get(link).isFeed()
               ? Constants.get(version, ConstantKey.ATOM_LINK_FEED)
               : Constants.get(version, ConstantKey.ATOM_LINK_ENTRY)));
 
@@ -192,7 +196,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
       while (true) {
         final Map.Entry<Integer, XmlElement> linkInfo =
                 extractElement(reader, null,
-                        Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)), startDepth, 2, 2);
+                Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)), startDepth, 2, 2);
 
         startDepth = linkInfo.getKey();
 
@@ -243,7 +247,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
         try {
           final XmlElement inlineElement =
                   extractElement(link.getContentReader(), null,
-                          Collections.<String>singletonList(Constants.get(version, ConstantKey.INLINE)), 0, -1, -1).
+                  Collections.<String>singletonList(Constants.get(version, ConstantKey.INLINE)), 0, -1, -1).
                   getValue();
           final XMLEventReader inlineReader = inlineElement.getContentReader();
 
@@ -447,7 +451,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
       // check edit link existence
       extractElement(reader, writer, Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)),
               Collections.<Map.Entry<String, String>>singletonList(
-                      new AbstractMap.SimpleEntry<String, String>("rel", "edit")), false, 0, -1, -1);
+              new AbstractMap.SimpleEntry<String, String>("rel", "edit")), false, 0, -1, -1);
 
       addAtomElement(IOUtils.toInputStream(editLinkElement), writer);
       writer.add(reader);
@@ -540,7 +544,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
         try {
           final XmlElement entryElement =
                   extractElement(reader, writer, Collections.<String>singletonList(
-                                  Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3).getValue();
+                  Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3).getValue();
 
           addAtomElement(
                   IOUtils.toInputStream("<content type=\"application/xml\">"),
@@ -795,7 +799,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
       if (event.getEventType() == XMLStreamConstants.START_ELEMENT
               && Constants.get(version, ConstantKey.LINK).equals(event.asStartElement().getName().getLocalPart())
               && !fieldToBeSaved.contains(
-                      event.asStartElement().getAttributeByName(new QName("title")).getValue())
+              event.asStartElement().getAttributeByName(new QName("title")).getValue())
               && !"edit".equals(event.asStartElement().getAttributeByName(new QName("rel")).getValue())) {
         writeCurrent = false;
       } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
@@ -803,13 +807,13 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
         writeNext = true;
       } else if (event.getEventType() == XMLStreamConstants.START_ELEMENT
               && (Constants.get(version, ConstantKey.PROPERTIES)).equals(
-                      event.asStartElement().getName().getLocalPart())) {
+              event.asStartElement().getName().getLocalPart())) {
         writeCurrent = true;
         writeNext = false;
         inProperties = true;
       } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
               && (Constants.get(version, ConstantKey.PROPERTIES)).equals(
-                      event.asEndElement().getName().getLocalPart())) {
+              event.asEndElement().getName().getLocalPart())) {
         writeCurrent = true;
       } else if (inProperties) {
         if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
@@ -826,7 +830,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
         } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
                 && StringUtils.isNotBlank(currentName)
                 && (Constants.get(version, ConstantKey.ATOM_PROPERTY_PREFIX) + currentName.trim()).equals(
-                        event.asEndElement().getName().getLocalPart())) {
+                event.asEndElement().getName().getLocalPart())) {
           writeNext = false;
           currentName = null;
         }
@@ -894,10 +898,10 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
 
         final XmlElement entry =
                 extractElement(
-                        getEventReader(readEntity(uri.getKey(), uri.getValue(), Accept.ATOM).getValue()),
-                        null,
-                        Collections.<String>singletonList("entry"),
-                        0, 1, 1).getValue();
+                getEventReader(readEntity(uri.getKey(), uri.getValue(), Accept.ATOM).getValue()),
+                null,
+                Collections.<String>singletonList("entry"),
+                0, 1, 1).getValue();
 
         IOUtils.copy(entry.toStream(), writer, encoding);
       } catch (Exception e) {
@@ -934,7 +938,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
 
     final Map.Entry<Integer, XmlElement> propertyElement =
             extractElement(reader, null,
-                    Collections.<String>singletonList(Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3);
+            Collections.<String>singletonList(Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3);
     reader.close();
 
     reader = propertyElement.getValue().getContentReader();
@@ -958,7 +962,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
       while (true) {
         final Map.Entry<Integer, XmlElement> linkElement =
                 extractElement(reader, null,
-                        Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)), pos, 2, 2);
+                Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)), pos, 2, 2);
 
         res.put("[Constants.get(version, ConstantKey.LINK)]"
                 + linkElement.getValue().getStart().getAttributeByName(new QName("title")).getValue(),
@@ -988,7 +992,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
     // ---------------------------------
     Map.Entry<Integer, XmlElement> propertyElement =
             extractElement(reader, writer,
-                    Collections.<String>singletonList(Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3);
+            Collections.<String>singletonList(Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3);
 
     writer.flush();
 
@@ -1121,9 +1125,9 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
     try {
       final XmlElement linkElement =
               extractElement(reader, writer,
-                      Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)),
-                      Collections.<Map.Entry<String, String>>singletonList(
-                              new SimpleEntry<String, String>("title", linkName)), false, 0, -1, -1).getValue();
+              Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)),
+              Collections.<Map.Entry<String, String>>singletonList(
+              new SimpleEntry<String, String>("title", linkName)), false, 0, -1, -1).getValue();
       writer.add(linkElement.getStart());
 
       // ------------------------------------------
@@ -1234,7 +1238,7 @@ public abstract class AbstractXMLUtilities extends AbstractUtilities {
 
     final InputStream src =
             fsManager.readFile(Commons.getEntityBasePath(entitySetName, entityId)
-                    + Constants.get(version, ConstantKey.ENTITY), Accept.XML);
+            + Constants.get(version, ConstantKey.ENTITY), Accept.XML);
 
     final XMLEventReader reader = getEventReader(src);
     final XmlElement property = extractElement(reader, null, pathElements, 0, 3, 4).getValue();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java b/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
index 2371843..cac8e0b 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
@@ -21,6 +21,7 @@ package org.apache.olingo.fit.utils;
 import java.util.regex.Pattern;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.entity.ContentType;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 import org.apache.olingo.fit.UnsupportedMediaTypeException;
 
 public enum Accept {
@@ -55,17 +56,17 @@ public enum Accept {
     this.fileExtension = fileExtension;
   }
 
-  public String toString(final ODataVersion version) {
-    return ODataVersion.v3 == version ? contentTypeV3 : contentTypeV4;
+  public String toString(final ODataServiceVersion version) {
+    return version.compareTo(ODataServiceVersion.V40) >= 0 ? contentTypeV4 : contentTypeV3;
   }
 
   public String getExtension() {
     return fileExtension;
   }
 
-  public static Accept parse(final String contentType, final ODataVersion version) {
+  public static Accept parse(final String contentType, final ODataServiceVersion version) {
     final Accept def;
-    if (ODataVersion.v3 == version) {
+    if (version.compareTo(ODataServiceVersion.V30) <= 0) {
       def = ATOM;
     } else {
       def = JSON_NOMETA;
@@ -74,7 +75,7 @@ public enum Accept {
     return parse(contentType, version, def);
   }
 
-  public static Accept parse(final String contentType, final ODataVersion version, final Accept def) {
+  public static Accept parse(final String contentType, final ODataServiceVersion version, final Accept def) {
     if (StringUtils.isBlank(contentType) || allTypesPattern.matcher(contentType).matches()) {
       return def;
     } else if (JSON_NOMETA.toString(version).equals(contentType)) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java b/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
index 89aaf97..d4f6267 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
@@ -18,6 +18,8 @@
  */
 package org.apache.olingo.fit.utils;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.InjectableValues;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -39,6 +41,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 import org.apache.commons.io.IOUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.core.data.AtomDeserializer;
+import org.apache.olingo.commons.core.data.AtomSerializer;
+import org.apache.olingo.commons.core.op.InjectableSerializerProvider;
+import org.apache.olingo.fit.metadata.Metadata;
 import org.codehaus.plexus.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -50,15 +57,24 @@ public abstract class Commons {
    */
   protected static final Logger LOG = LoggerFactory.getLogger(Commons.class);
 
+  private static Map<ODataServiceVersion, AtomDeserializer> atomDeserializer =
+          new EnumMap<ODataServiceVersion, AtomDeserializer>(ODataServiceVersion.class);
+
+  private static Map<ODataServiceVersion, AtomSerializer> atomSerializer =
+          new EnumMap<ODataServiceVersion, AtomSerializer>(ODataServiceVersion.class);
+
+  private static Map<ODataServiceVersion, ObjectMapper> jsonmapper =
+          new EnumMap<ODataServiceVersion, ObjectMapper>(ODataServiceVersion.class);
+
+  private static EnumMap<ODataServiceVersion, Metadata> metadata =
+          new EnumMap<ODataServiceVersion, Metadata>(ODataServiceVersion.class);
+
   protected static Pattern multiKeyPattern = Pattern.compile("(.*=.*,?)+");
 
   protected final static Map<String, Integer> sequence = new HashMap<String, Integer>();
 
   protected final static Map<String, String> mediaContent = new HashMap<String, String>();
 
-  protected final static Map<ODataVersion, MetadataLinkInfo> linkInfo =
-          new EnumMap<ODataVersion, MetadataLinkInfo>(ODataVersion.class);
-
   static {
     sequence.put("Customer", 1000);
     sequence.put("CustomerInfo", 1000);
@@ -75,8 +91,56 @@ public abstract class Commons {
     mediaContent.put("Car/Photo", null);
   }
 
-  public static Map<ODataVersion, MetadataLinkInfo> getLinkInfo() {
-    return linkInfo;
+  public static AtomDeserializer getAtomDeserializer(final ODataServiceVersion version) {
+    if (!atomDeserializer.containsKey(version)) {
+      atomDeserializer.put(version, new AtomDeserializer(version));
+    }
+    return atomDeserializer.get(version);
+  }
+
+  public static AtomSerializer getAtomSerializer(final ODataServiceVersion version) {
+    if (!atomSerializer.containsKey(version)) {
+      atomSerializer.put(version, new AtomSerializer(version, true));
+    }
+
+    return atomSerializer.get(version);
+  }
+
+  public static ObjectMapper getJsonMapper(final ODataServiceVersion version) {
+    if (!jsonmapper.containsKey(version)) {
+      final ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
+
+      mapper.setInjectableValues(new InjectableValues.Std()
+              .addValue(Boolean.class, Boolean.TRUE)
+              .addValue(ODataServiceVersion.class, version));
+
+      mapper.setSerializerProvider(new InjectableSerializerProvider(mapper.getSerializerProvider(),
+              mapper.getSerializationConfig()
+              .withAttribute(ODataServiceVersion.class, version)
+              .withAttribute(Boolean.class, Boolean.TRUE),
+              mapper.getSerializerFactory()));
+
+      jsonmapper.put(version, mapper);
+    }
+
+    return jsonmapper.get(version);
+  }
+
+  public static Metadata getMetadata(final ODataServiceVersion version) {
+    if (!metadata.containsKey(version)) {
+      final InputStream is = Commons.class.getResourceAsStream(
+              File.separatorChar
+              + version.name()
+              + File.separatorChar + "metadata.xml");
+
+      metadata.put(version, new Metadata(is));
+    }
+
+    return metadata.get(version);
+  }
+
+  public static Map<String, String> getMediaContent() {
+    return mediaContent;
   }
 
   public static String getEntityURI(final String entitySetName, final String entityKey) {
@@ -91,7 +155,7 @@ public abstract class Commons {
   }
 
   public static String getLinksURI(
-          final ODataVersion version,
+          final ODataServiceVersion version,
           final String entitySetName,
           final String entityId,
           final String linkName) throws IOException {
@@ -99,22 +163,22 @@ public abstract class Commons {
   }
 
   public static String getLinksPath(
-          final ODataVersion version,
+          final ODataServiceVersion version,
           final String entitySetName,
           final String entityId,
           final String linkName,
           final Accept accept) throws IOException {
-    return getLinksPath(ODataVersion.v3, getEntityBasePath(entitySetName, entityId), linkName, accept);
+    return getLinksPath(ODataServiceVersion.V30, getEntityBasePath(entitySetName, entityId), linkName, accept);
 
   }
 
   public static String getLinksPath(
-          final ODataVersion version, final String basePath, final String linkName, final Accept accept)
+          final ODataServiceVersion version, final String basePath, final String linkName, final Accept accept)
           throws IOException {
     try {
       return FSManager.instance(version)
               .getAbsolutePath(basePath + Constants.get(version, ConstantKey.LINKS_FILE_PATH)
-                      + File.separatorChar + linkName, accept);
+              + File.separatorChar + linkName, accept);
     } catch (Exception e) {
       throw new IOException(e);
     }
@@ -255,7 +319,7 @@ public abstract class Commons {
     return node;
   }
 
-  public static String getETag(final String basePath, final ODataVersion version) throws Exception {
+  public static String getETag(final String basePath, final ODataServiceVersion version) throws Exception {
     try {
       final InputStream is = FSManager.instance(version).readFile(basePath + "etag", Accept.TEXT);
       if (is.available() <= 0) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java b/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
index ffd52bc..5066cb0 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
@@ -18,8 +18,10 @@
  */
 package org.apache.olingo.fit.utils;
 
+import java.nio.charset.Charset;
 import java.util.EnumMap;
 import java.util.Map;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 
 public class Constants {
 
@@ -27,6 +29,8 @@ public class Constants {
 
   private final static Map<ConstantKey, String> constants = new EnumMap<ConstantKey, String>(ConstantKey.class);
 
+  public static Charset encoding = Charset.forName("UTF-8");
+
   static {
 
     // -----------------------------
@@ -93,8 +97,8 @@ public class Constants {
     return get(null, key);
   }
 
-  public static String get(final ODataVersion version, final ConstantKey key) {
-    return (version == null || version == ODataVersion.v3 || !v4constants.containsKey(key)
+  public static String get(final ODataServiceVersion version, final ConstantKey key) {
+    return (version == null || version.compareTo(ODataServiceVersion.V30) <= 0 || !v4constants.containsKey(key)
             ? constants : v4constants).get(key);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/DataBinder.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/DataBinder.java b/fit/src/main/java/org/apache/olingo/fit/utils/DataBinder.java
new file mode 100644
index 0000000..6eda42e
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/DataBinder.java
@@ -0,0 +1,276 @@
+/*
+ * 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.fit.utils;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.commons.api.data.Entry;
+import org.apache.olingo.commons.api.data.Feed;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.Value;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.core.data.AtomEntryImpl;
+import org.apache.olingo.commons.core.data.AtomFeedImpl;
+import org.apache.olingo.commons.core.data.AtomPropertyImpl;
+import org.apache.olingo.commons.core.data.CollectionValueImpl;
+import org.apache.olingo.commons.core.data.ComplexValueImpl;
+import org.apache.olingo.commons.core.data.JSONEntryImpl;
+import org.apache.olingo.commons.core.data.JSONFeedImpl;
+import org.apache.olingo.commons.core.data.JSONPropertyImpl;
+import org.apache.olingo.commons.core.data.LinkImpl;
+import org.apache.olingo.fit.metadata.Metadata;
+import org.apache.olingo.fit.metadata.NavigationProperty;
+import org.springframework.beans.BeanUtils;
+
+public class DataBinder {
+
+  private final ODataServiceVersion version;
+
+  public DataBinder(final ODataServiceVersion version) {
+    this.version = version;
+  }
+
+  public JSONFeedImpl getJsonFeed(final AtomFeedImpl atomfeed) {
+    final JSONFeedImpl jsonfeed = new JSONFeedImpl();
+
+    BeanUtils.copyProperties(atomfeed, jsonfeed, "baseURI", "metadataContextURL");
+    jsonfeed.setMetadataContextURL(atomfeed.getBaseURI() == null
+            ? null
+            : URI.create(atomfeed.getBaseURI().toASCIIString() + "/$metadata").normalize());
+
+    final Collection<Entry> entries = jsonfeed.getEntries();
+    for (Entry entry : atomfeed.getEntries()) {
+      entries.add(getJsonEntry((AtomEntryImpl) entry));
+    }
+
+    return jsonfeed;
+  }
+
+  public AtomFeedImpl getAtomFeed(final JSONFeedImpl jsonfeed) {
+    final AtomFeedImpl atomfeed = new AtomFeedImpl();
+
+    BeanUtils.copyProperties(jsonfeed, atomfeed, "baseURI", "metadataContextURL");
+    atomfeed.setBaseURI(jsonfeed.getBaseURI() == null
+            ? null
+            : jsonfeed.getBaseURI().toASCIIString() + "/$metadata");
+
+    final Collection<Entry> entries = atomfeed.getEntries();
+    for (Entry entry : jsonfeed.getEntries()) {
+      entries.add(getAtomEntry((JSONEntryImpl) entry));
+    }
+
+    return atomfeed;
+  }
+
+  public JSONEntryImpl getJsonEntry(final AtomEntryImpl atomentry) {
+    final JSONEntryImpl jsonentry = new JSONEntryImpl();
+
+    BeanUtils.copyProperties(atomentry, jsonentry, "baseURI", "properties", "links");
+    jsonentry.setBaseURI(atomentry.getBaseURI() == null ? null : atomentry.getBaseURI().toASCIIString());
+
+    for (Link link : atomentry.getNavigationLinks()) {
+
+      final Link jlink = new LinkImpl();
+      jlink.setHref(link.getHref());
+      jlink.setTitle(link.getTitle());
+      jlink.setType(link.getType());
+      jlink.setRel(link.getRel());
+
+      if (link.getInlineEntry() instanceof AtomEntryImpl) {
+        Entry inlineEntry = link.getInlineEntry();
+        if (inlineEntry instanceof AtomEntryImpl) {
+          jlink.setInlineEntry(getJsonEntry((AtomEntryImpl) link.getInlineEntry()));
+        }
+      } else if (link.getInlineFeed() instanceof AtomFeedImpl) {
+
+        Feed inlineFeed = link.getInlineFeed();
+        if (inlineFeed instanceof AtomFeedImpl) {
+          jlink.setInlineFeed(getJsonFeed((AtomFeedImpl) link.getInlineFeed()));
+        }
+      }
+
+      jsonentry.getNavigationLinks().add(jlink);
+    }
+
+    final Collection<Property> properties = jsonentry.getProperties();
+    for (Property property : atomentry.getProperties()) {
+      properties.add(getJsonProperty((AtomPropertyImpl) property));
+    }
+
+    return jsonentry;
+  }
+
+  public AtomEntryImpl getAtomEntry(final JSONEntryImpl jsonentry) {
+    final AtomEntryImpl atomentry = new AtomEntryImpl();
+
+    final Metadata metadata = Commons.getMetadata(version);
+
+    BeanUtils.copyProperties(jsonentry, atomentry, "baseURI", "properties", "links");
+    atomentry.setBaseURI(jsonentry.getBaseURI() == null ? null : jsonentry.getBaseURI().toASCIIString());
+
+    for (Link link : jsonentry.getNavigationLinks()) {
+      final Link alink = new LinkImpl();
+      alink.setHref(link.getHref());
+      alink.setTitle(link.getTitle());
+      alink.setType(metadata.getEntityType(jsonentry.getType()).getNavigationProperty(link.getTitle()).isFeed()
+              ? Constants.get(ConstantKey.ATOM_LINK_FEED) : Constants.get(ConstantKey.ATOM_LINK_ENTRY));
+      alink.setRel(link.getRel());
+
+      if (link.getInlineEntry() instanceof JSONEntryImpl) {
+        Entry inlineEntry = link.getInlineEntry();
+        if (inlineEntry instanceof JSONEntryImpl) {
+          alink.setInlineEntry(getAtomEntry((JSONEntryImpl) link.getInlineEntry()));
+        }
+      } else if (link.getInlineFeed() instanceof JSONFeedImpl) {
+        Feed inlineFeed = link.getInlineFeed();
+        if (inlineFeed instanceof JSONFeedImpl) {
+          alink.setInlineFeed(getAtomFeed((JSONFeedImpl) link.getInlineFeed()));
+        }
+      }
+
+      atomentry.getNavigationLinks().add(alink);
+    }
+
+    final Map<String, NavigationProperty> navProperties =
+            metadata.getEntityType(jsonentry.getType()).getNavigationPropertyMap();
+
+    final List<Property> properties = atomentry.getProperties();
+
+    for (Property property : jsonentry.getProperties()) {
+      if (navProperties.containsKey(property.getName())) {
+        final Link alink = new LinkImpl();
+        alink.setTitle(property.getName());
+
+        alink.setType(navProperties.get(property.getName()).isFeed()
+                ? Constants.get(ConstantKey.ATOM_LINK_FEED)
+                : Constants.get(ConstantKey.ATOM_LINK_ENTRY));
+
+        alink.setRel(Constants.get(ConstantKey.ATOM_LINK_REL) + property.getName());
+
+        if (property.getValue().isComplex()) {
+          final Entry inline = new AtomEntryImpl();
+          inline.setType(navProperties.get(property.getName()).getType());
+          for (Property prop : property.getValue().asComplex().get()) {
+            inline.getProperties().add(prop);
+          }
+          alink.setInlineEntry(inline);
+
+        } else if (property.getValue().isCollection()) {
+          final Feed inline = new AtomFeedImpl();
+          for (Value entry : property.getValue().asCollection().get()) {
+            final Entry inlineEntry = new AtomEntryImpl();
+            inlineEntry.setType(navProperties.get(property.getName()).getType());
+            for (Property prop : entry.asComplex().get()) {
+              inlineEntry.getProperties().add(prop);
+            }
+            inline.getEntries().add(inlineEntry);
+          }
+          alink.setInlineFeed(inline);
+        } else {
+          throw new IllegalStateException("Invalid navigation property " + property);
+        }
+        atomentry.getNavigationLinks().add(alink);
+      } else {
+        properties.add(getAtomProperty((JSONPropertyImpl) property, atomentry.getType()));
+      }
+    }
+
+    return atomentry;
+  }
+
+  public JSONPropertyImpl getJsonProperty(final AtomPropertyImpl atomproperty) {
+    final JSONPropertyImpl jsonproperty = new JSONPropertyImpl();
+    BeanUtils.copyProperties(atomproperty, jsonproperty, "value");
+
+    if (atomproperty.getValue() instanceof ComplexValueImpl) {
+      final ComplexValueImpl complex = new ComplexValueImpl();
+      jsonproperty.setValue(complex);
+
+      for (Property field : atomproperty.getValue().asComplex().get()) {
+        complex.get().add(getJsonProperty((AtomPropertyImpl) field));
+      }
+    } else if (atomproperty.getValue() instanceof CollectionValueImpl) {
+      final CollectionValueImpl collection = new CollectionValueImpl();
+      jsonproperty.setValue(collection);
+
+      for (Value element : atomproperty.getValue().asCollection().get()) {
+        if (element instanceof ComplexValueImpl) {
+          final ComplexValueImpl complex = new ComplexValueImpl();
+          collection.get().add(complex);
+
+          for (Property field : element.asComplex().get()) {
+            complex.get().add(getJsonProperty((AtomPropertyImpl) field));
+          }
+        } else {
+          collection.get().add(element);
+        }
+      }
+    } else {
+      jsonproperty.setValue(atomproperty.getValue());
+    }
+
+    return jsonproperty;
+  }
+
+  public AtomPropertyImpl getAtomProperty(final JSONPropertyImpl jsonproperty, final String entryType) {
+    final AtomPropertyImpl atomproperty = new AtomPropertyImpl();
+    atomproperty.setName(jsonproperty.getName());
+
+    if (StringUtils.isNotBlank(jsonproperty.getType())) {
+      atomproperty.setType(jsonproperty.getType());
+    } else {
+      atomproperty.setType(
+              Commons.getMetadata(version).getEntityType(entryType).getProperty(jsonproperty.getName()).getType());
+    }
+
+    if (jsonproperty.getValue() instanceof ComplexValueImpl) {
+      final ComplexValueImpl complex = new ComplexValueImpl();
+      atomproperty.setValue(complex);
+
+      for (Property field : jsonproperty.getValue().asComplex().get()) {
+        complex.get().add(getAtomProperty((JSONPropertyImpl) field, atomproperty.getType()));
+      }
+    } else if (jsonproperty.getValue() instanceof CollectionValueImpl) {
+      final CollectionValueImpl collection = new CollectionValueImpl();
+      atomproperty.setValue(collection);
+
+      for (Value element : jsonproperty.getValue().asCollection().get()) {
+        if (element instanceof ComplexValueImpl) {
+          final ComplexValueImpl complex = new ComplexValueImpl();
+          collection.get().add(complex);
+
+          for (Property field : element.asComplex().get()) {
+            complex.get().add(getAtomProperty((JSONPropertyImpl) field,
+                    atomproperty.getType().replaceAll("^Collection\\(", "").replaceAll("\\)$", "")));
+          }
+        } else {
+          collection.get().add(element);
+        }
+      }
+    } else {
+      atomproperty.setValue(jsonproperty.getValue());
+    }
+
+    return atomproperty;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java b/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java
index 070f2b3..d1543d5 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java
@@ -18,10 +18,14 @@
  */
 package org.apache.olingo.fit.utils;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.util.EnumMap;
 import java.util.Map;
 import javax.ws.rs.NotFoundException;
@@ -32,6 +36,12 @@ import org.apache.commons.vfs2.FileSelector;
 import org.apache.commons.vfs2.FileSystemException;
 import org.apache.commons.vfs2.FileSystemManager;
 import org.apache.commons.vfs2.VFS;
+import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.core.data.AtomEntryImpl;
+import org.apache.olingo.commons.core.data.AtomSerializer;
+import org.apache.olingo.commons.core.data.JSONEntryImpl;
+import org.apache.olingo.fit.serializer.JsonEntryContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,18 +58,19 @@ public class FSManager {
 
   private final FileSystemManager fsManager;
 
-  private static Map<ODataVersion, FSManager> instance = new EnumMap<ODataVersion, FSManager>(ODataVersion.class);
+  private static Map<ODataServiceVersion, FSManager> instance =
+          new EnumMap<ODataServiceVersion, FSManager>(ODataServiceVersion.class);
 
-  private final ODataVersion version;
+  private final ODataServiceVersion version;
 
-  public static FSManager instance(final ODataVersion version) throws Exception {
+  public static FSManager instance(final ODataServiceVersion version) throws Exception {
     if (!instance.containsKey(version)) {
       instance.put(version, new FSManager(version));
     }
     return instance.get(version);
   }
 
-  private FSManager(final ODataVersion version) throws Exception {
+  private FSManager(final ODataServiceVersion version) throws Exception {
     this.version = version;
     fsManager = VFS.getManager();
   }
@@ -88,6 +99,37 @@ public class FSManager {
     return memObject;
   }
 
+  public void putInMemory(final Container<AtomEntryImpl> container, final String relativePath)
+          throws IOException {
+    try {
+      final ODataServiceVersion serviceVersion =
+              version == ODataServiceVersion.V30 ? ODataServiceVersion.V30 : ODataServiceVersion.V40;
+
+      final AtomSerializer atomSerializer = Commons.getAtomSerializer(serviceVersion);
+
+      final ByteArrayOutputStream content = new ByteArrayOutputStream();
+      final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.encoding);
+
+      atomSerializer.write(writer, container);
+      writer.flush();
+
+      putInMemory(new ByteArrayInputStream(content.toByteArray()), getAbsolutePath(relativePath, Accept.ATOM));
+      content.reset();
+
+      final ObjectMapper mapper = Commons.getJsonMapper(serviceVersion);
+      mapper.writeValue(
+              writer, new JsonEntryContainer<JSONEntryImpl>(
+              container.getContextURL(),
+              container.getMetadataETag(),
+              (new DataBinder(version == ODataServiceVersion.V30 ? ODataServiceVersion.V30 : ODataServiceVersion.V40)).
+              getJsonEntry(container.getObject())));
+
+      putInMemory(new ByteArrayInputStream(content.toByteArray()), getAbsolutePath(relativePath, Accept.JSON_FULLMETA));
+    } catch (Exception e) {
+      throw new IOException(e);
+    }
+  }
+
   public InputStream readFile(final String relativePath) {
     return readFile(relativePath, null);
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/ODataVersion.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/ODataVersion.java b/fit/src/main/java/org/apache/olingo/fit/utils/ODataVersion.java
deleted file mode 100644
index 5b35d4d..0000000
--- a/fit/src/main/java/org/apache/olingo/fit/utils/ODataVersion.java
+++ /dev/null
@@ -1,35 +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.fit.utils;
-
-public enum ODataVersion {
-
-  v3("3.0"),
-  v4("4.0");
-
-  private final String version;
-
-  private ODataVersion(String version) {
-    this.version = version;
-  }
-
-  public String getVersion() {
-    return version;
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java
index 0f2a3f2..24b6c55 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java
@@ -18,11 +18,11 @@
  */
 package org.apache.olingo.fit.utils.v3;
 
-import org.apache.olingo.fit.utils.ODataVersion;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 
 public class JSONUtilities extends org.apache.olingo.fit.utils.AbstractJSONUtilities {
 
   public JSONUtilities() throws Exception {
-    super(ODataVersion.v3);
+    super(ODataServiceVersion.V30);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java
index 919cee6..2eb0983 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java
@@ -18,175 +18,11 @@
  */
 package org.apache.olingo.fit.utils.v3;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.AbstractMap.SimpleEntry;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import javax.xml.namespace.QName;
-import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.events.StartElement;
-import org.apache.commons.io.IOUtils;
-import org.apache.olingo.fit.utils.Accept;
-import org.apache.olingo.fit.utils.Commons;
-import org.apache.olingo.fit.utils.ConstantKey;
-import org.apache.olingo.fit.utils.Constants;
-import org.apache.olingo.fit.utils.MetadataLinkInfo;
-import org.apache.olingo.fit.utils.ODataVersion;
-import org.apache.olingo.fit.utils.XmlElement;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 
 public class XMLUtilities extends org.apache.olingo.fit.utils.AbstractXMLUtilities {
 
   public XMLUtilities() throws Exception {
-    super(ODataVersion.v3);
-  }
-
-  @Override
-  public void retrieveLinkInfoFromMetadata() throws Exception {
-
-    final MetadataLinkInfo metadataLinkInfo = new MetadataLinkInfo();
-    Commons.getLinkInfo().put(version, metadataLinkInfo);
-
-    final InputStream metadata = fsManager.readFile(Constants.get(version, ConstantKey.METADATA), Accept.XML);
-    final XMLEventReader reader = getEventReader(metadata);
-
-    try {
-      while (true) {
-        final Map.Entry<Integer, XmlElement> entitySetElement =
-                extractElement(reader, null, Collections.<String>singletonList("EntitySet"),
-                null, false, 0, -1, -1);
-
-        retrieveLinks(entitySetElement.getValue(), metadataLinkInfo);
-      }
-    } catch (Exception e) {
-    } finally {
-      reader.close();
-    }
-  }
-
-  private void retrieveLinks(final XmlElement entitySetElement, final MetadataLinkInfo metadataLinkInfo)
-          throws Exception {
-
-    final InputStream metadata = fsManager.readFile(Constants.get(version, ConstantKey.METADATA), Accept.XML);
-
-    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    IOUtils.copy(metadata, bos);
-    IOUtils.closeQuietly(metadata);
-
-    final String entitySetName = entitySetElement.getStart().getAttributeByName(new QName("Name")).getValue().trim();
-    final String entityType = entitySetElement.getStart().getAttributeByName(new QName("EntityType")).getValue().trim();
-
-    final Collection<Map.Entry<String, String>> filter = new HashSet<Map.Entry<String, String>>();
-    filter.add(new SimpleEntry<String, String>(
-            "Name", entityType.substring(entityType.lastIndexOf(".") + 1, entityType.length())));
-    filter.add(new SimpleEntry<String, String>("BaseType", entityType));
-
-    final XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
-
-    final Map.Entry<Integer, XmlElement> entityTypeElement = extractElement(
-            reader, null, Collections.<String>singletonList("EntityType"), filter, true, 0, -1, -1);
-
-    final XMLEventReader entityReader = entityTypeElement.getValue().getContentReader();
-    int size = 0;
-
-    try {
-      while (true) {
-        final XmlElement navProperty =
-                extractElement(entityReader, null, Collections.<String>singletonList("NavigationProperty"),
-                null, false, 0, -1, -1).getValue();
-
-        final String linkName = navProperty.getStart().getAttributeByName(new QName("Name")).getValue();
-        final Map.Entry<String, Boolean> target = getTargetInfo(navProperty.getStart(), linkName);
-
-        metadataLinkInfo.addLink(
-                entitySetName,
-                linkName,
-                target.getKey(),
-                target.getValue());
-
-        size++;
-      }
-    } catch (Exception e) {
-    } finally {
-      entityReader.close();
-    }
-
-    if (size == 0) {
-      metadataLinkInfo.addEntitySet(entitySetName);
-    }
-  }
-
-  private Map.Entry<String, Boolean> getTargetInfo(final StartElement element, final String linkName)
-          throws Exception {
-    final InputStream metadata = fsManager.readFile(Constants.get(version, ConstantKey.METADATA), Accept.XML);
-
-    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    IOUtils.copy(metadata, bos);
-    IOUtils.closeQuietly(metadata);
-
-    // ------------------------------------
-    // Retrieve association
-    // ------------------------------------
-    XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
-
-    final String associationName = element.getAttributeByName(new QName("Relationship")).getValue();
-
-    final Map.Entry<Integer, XmlElement> association = extractElement(
-            reader, null, Collections.<String>singletonList("Association"),
-            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>(
-            "Name", associationName.substring(associationName.lastIndexOf(".") + 1))), false,
-            0, 4, 4);
-
-    reader.close();
-    // ------------------------------------
-
-    // ------------------------------------
-    // check for feed or not from Association role
-    // ------------------------------------
-    InputStream associationContent = association.getValue().toStream();
-    reader = getEventReader(associationContent);
-
-    Map.Entry<Integer, XmlElement> associationEnd = extractElement(
-            reader, null, Collections.<String>singletonList("End"),
-            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>("Role", linkName)),
-            false, 0, -1, -1);
-
-    reader.close();
-    IOUtils.closeQuietly(associationContent);
-
-    final boolean feed = associationEnd.getValue().getStart().getAttributeByName(
-            new QName("Multiplicity")).getValue().equals("*");
-    // ------------------------------------
-
-    // ------------------------------------
-    // Retrieve target association set name
-    // ------------------------------------
-    reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
-
-    final Map.Entry<Integer, XmlElement> associationSet = extractElement(
-            reader, null, Collections.<String>singletonList("AssociationSet"),
-            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>(
-            "Association", associationName)), false, 0, -1, -1);
-
-    reader.close();
-
-    associationContent = associationSet.getValue().toStream();
-    reader = getEventReader(associationContent);
-
-    associationEnd = extractElement(
-            reader, null, Collections.<String>singletonList("End"),
-            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>("Role", linkName)),
-            false, 0, -1, -1);
-
-    reader.close();
-    IOUtils.closeQuietly(associationContent);
-
-    final String target = associationEnd.getValue().getStart().getAttributeByName(new QName("EntitySet")).getValue();
-    // ------------------------------------
-
-    return new SimpleEntry<String, Boolean>(target, feed);
+    super(ODataServiceVersion.V30);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/450ccfd4/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java
index daf75f4..9b2d607 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java
@@ -18,11 +18,11 @@
  */
 package org.apache.olingo.fit.utils.v4;
 
-import org.apache.olingo.fit.utils.ODataVersion;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 
 public class JSONUtilities extends org.apache.olingo.fit.utils.AbstractJSONUtilities {
 
   public JSONUtilities() throws Exception {
-    super(ODataVersion.v4);
+    super(ODataServiceVersion.V40);
   }
 }