[OLINGO-129] Merge with master branch
diff --git a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/datasource/AnnotationDataSource.java b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/datasource/AnnotationDataSource.java
index b2b44ba..eb22f81 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/datasource/AnnotationDataSource.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/datasource/AnnotationDataSource.java
@@ -151,7 +151,12 @@
 
     AnnotatedNavInfo navInfo = ANNOTATION_HELPER.getCommonNavigationInfo(
         sourceStore.getDataTypeClass(), targetStore.getDataTypeClass());
-    Field sourceField = navInfo.getFromField();
+    final Field sourceField;
+    if(navInfo.isBiDirectional()) {
+      sourceField = navInfo.getToField();
+    } else {
+      sourceField = navInfo.getFromField();
+    }
     if (sourceField == null) {
       throw new AnnotationRuntimeException("Missing source field for related data (sourceStore='" + sourceStore
           + "', targetStore='" + targetStore + "').");
diff --git a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java
index 00ef56b..21df18a 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProvider.java
@@ -374,9 +374,13 @@
         }
         EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
         if (enp != null) {
-          final NavigationProperty navProperty = createNavigationProperty(namespace, enp, field);
+          Class<?> fromClass = field.getDeclaringClass();
+          Class<?> toClass = ClassHelper.getFieldType(field);
+          AnnotationHelper.AnnotatedNavInfo info = ANNOTATION_HELPER.getCommonNavigationInfo(fromClass, toClass);
+
+          final NavigationProperty navProperty = createNavigationProperty(namespace, field, info);
           navProperties.add(navProperty);
-          Association association = createAssociation(field, navProperty);
+          Association association = createAssociation(info);
           associations.add(association);
         }
         EdmMediaResourceContent emrc = field.getAnnotation(EdmMediaResourceContent.class);
@@ -498,17 +502,15 @@
       return cp;
     }
 
-    private NavigationProperty createNavigationProperty(final String namespace, final EdmNavigationProperty enp,
-        final Field field) {
+    private NavigationProperty createNavigationProperty(final String namespace, Field field,
+                                                        AnnotationHelper.AnnotatedNavInfo navInfo) {
       NavigationProperty navProp = new NavigationProperty();
       navProp.setName(ANNOTATION_HELPER.getPropertyName(field));
-      String fromRole = ANNOTATION_HELPER.extractFromRoleName(enp, field);
+      String fromRole = navInfo.getFromRoleName();
       navProp.setFromRole(fromRole);
+      navProp.setToRole(navInfo.getToRoleName());
 
-      String toRole = ANNOTATION_HELPER.extractToRoleName(enp, field);
-      navProp.setToRole(toRole);
-
-      String relationshipName = ANNOTATION_HELPER.extractRelationshipName(enp, field);
+      String relationshipName = navInfo.getRelationshipName();
       navProp.setRelationship(new FullQualifiedName(namespace, relationshipName));
 
       return navProp;
@@ -600,25 +602,22 @@
       return ANNOTATION_HELPER.extractEntityTypeFqn(baseEntityClass);
     }
 
-    private Association createAssociation(final Field field, final NavigationProperty navProperty) {
+    private Association createAssociation(final AnnotationHelper.AnnotatedNavInfo info) {
       Association association = new Association();
-      EdmNavigationProperty navigation = field.getAnnotation(EdmNavigationProperty.class);
 
       AssociationEnd fromEnd = new AssociationEnd();
-      fromEnd.setRole(navProperty.getFromRole());
-      String typeName = ANNOTATION_HELPER.extractEntityTypeName(field.getDeclaringClass());
-      fromEnd.setType(new FullQualifiedName(namespace, typeName));
-      fromEnd.setMultiplicity(EdmMultiplicity.ONE);
+      fromEnd.setRole(info.getFromRoleName());
+      fromEnd.setType(new FullQualifiedName(namespace, info.getFromTypeName()));
+      fromEnd.setMultiplicity(info.getFromMultiplicity());
       association.setEnd1(fromEnd);
 
       AssociationEnd toEnd = new AssociationEnd();
-      toEnd.setRole(navProperty.getToRole());
-      String toTypeName = ANNOTATION_HELPER.extractEntitTypeName(navigation, field);
-      toEnd.setType(new FullQualifiedName(namespace, toTypeName));
-      toEnd.setMultiplicity(ANNOTATION_HELPER.getMultiplicity(navigation, field));
+      toEnd.setRole(info.getToRoleName());
+      toEnd.setType(new FullQualifiedName(namespace, info.getToTypeName()));
+      toEnd.setMultiplicity(info.getToMultiplicity());
       association.setEnd2(toEnd);
 
-      String associationName = navProperty.getRelationship().getName();
+      String associationName = info.getRelationshipName();
       association.setName(associationName);
       return association;
     }
diff --git a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java
index 0d2ff11..d0f072a 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelper.java
@@ -103,22 +103,18 @@
 
   private boolean isEqual(final Object firstKey, final Object secondKey) {
     if (firstKey == null) {
-      if (secondKey == null) {
-        return true;
-      } else {
-        return secondKey.equals(firstKey);
-      }
+      return secondKey == null || secondKey.equals(firstKey);
     } else {
       return firstKey.equals(secondKey);
     }
   }
 
-  public String extractEntitTypeName(final EdmNavigationProperty enp, final Class<?> fallbackClass) {
+  public String extractEntityTypeName(final EdmNavigationProperty enp, final Class<?> fallbackClass) {
     Class<?> entityTypeClass = enp.toType();
     return extractEntityTypeName(entityTypeClass == Object.class ? fallbackClass : entityTypeClass);
   }
 
-  public String extractEntitTypeName(final EdmNavigationProperty enp, final Field field) {
+  public String extractEntityTypeName(final EdmNavigationProperty enp, final Field field) {
     Class<?> entityTypeClass = enp.toType();
     if (entityTypeClass == Object.class) {
       Class<?> toClass = field.getType();
@@ -199,8 +195,7 @@
   }
 
   public String generateNamespace(final Class<?> annotatedClass) {
-    String packageName = annotatedClass.getPackage().getName();
-    return packageName;
+    return annotatedClass.getPackage().getName();
   }
 
   /**
@@ -263,40 +258,53 @@
     return propertyName;
   }
 
-  public String extractFromRoleName(final EdmNavigationProperty enp, final Field field) {
-    return getCanonicalRole(field.getDeclaringClass());
-  }
-
   public String extractToRoleName(final EdmNavigationProperty enp, final Field field) {
     String role = enp.toRole();
     if (role.isEmpty()) {
-      role = getCanonicalRole(
-          field.getType().isArray() || Collection.class.isAssignableFrom(field.getType()) ?
-              (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0] : field.getType());
+      role = getCanonicalRoleName(field.getName());
     }
     return role;
   }
 
-  public String getCanonicalRole(final Class<?> fallbackClass) {
-    String toRole = extractEntityTypeName(fallbackClass);
-    return "r_" + toRole;
+  public String extractFromRoleEntityName(final Field field) {
+    return extractEntityTypeName(field.getDeclaringClass());
+  }
+
+  public String extractToRoleEntityName(final EdmNavigationProperty enp, final Field field) {
+    Class<?> clazz = enp.toType();
+    if (clazz == Object.class) {
+      if(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType())) {
+        clazz = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+      } else {
+        clazz = field.getType();
+      }
+    }
+    return extractEntityTypeName(clazz);
+  }
+
+  public String getCanonicalRoleName(String name) {
+    return "r_" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
   }
 
   public String extractRelationshipName(final EdmNavigationProperty enp, final Field field) {
     String relationshipName = enp.association();
     if (relationshipName.isEmpty()) {
-      final String fromRole = extractFromRoleName(enp, field);
-      final String toRole = extractToRoleName(enp, field);
-      if (fromRole.compareTo(toRole) > 0) {
-        relationshipName = toRole + "-" + fromRole;
-      } else {
-        relationshipName = fromRole + "-" + toRole;
-      }
+      final String fromRole = extractFromRoleEntityName(field);
+      final String toRole = extractToRoleEntityName(enp, field);
+      return createCanonicalRelationshipName(fromRole, toRole);
     }
     return relationshipName;
   }
 
-  public EdmMultiplicity getMultiplicity(final EdmNavigationProperty enp, final Field field) {
+  public String createCanonicalRelationshipName(String fromRole, String toRole) {
+      if (fromRole.compareTo(toRole) > 0) {
+        return toRole + "-" + fromRole;
+      } else {
+        return fromRole + "-" + toRole;
+      }
+  }
+
+  public EdmMultiplicity extractMultiplicity(final EdmNavigationProperty enp, final Field field) {
     EdmMultiplicity multiplicity = mapMultiplicity(enp.toMultiplicity());
     final boolean isCollectionType = field.getType().isArray() || Collection.class.isAssignableFrom(field.getType());
 
@@ -357,48 +365,131 @@
       return fromField;
     }
 
+    public String getFromRoleName() {
+      if(isBiDirectional()) {
+        return extractFromRoleEntityName(toField);
+      }
+      return extractToRoleName(toNavigation, toField);
+    }
+
     public Field getToField() {
       return toField;
     }
 
+    public String getToRoleName() {
+      if(isBiDirectional()) {
+        return extractToRoleName(toNavigation, toField);
+      }
+      return extractToRoleName(fromNavigation, fromField);
+    }
+
     public EdmMultiplicity getFromMultiplicity() {
-      return getMultiplicity(toNavigation, toField);
+      if(isBiDirectional()) {
+        return EdmMultiplicity.ONE;
+      }
+      return extractMultiplicity(toNavigation, toField);
     }
 
     public EdmMultiplicity getToMultiplicity() {
-      return getMultiplicity(fromNavigation, fromField);
+      if(isBiDirectional()) {
+        return extractMultiplicity(toNavigation, toField);
+      }
+      return extractMultiplicity(fromNavigation, fromField);
     }
 
     public boolean isBiDirectional() {
-      return toNavigation != null;
+      return fromNavigation == null;
+    }
+
+    public String getRelationshipName() {
+      String toAssociation = toNavigation.association();
+      String fromAssociation = "";
+      if(!isBiDirectional()) {
+        fromAssociation = fromNavigation.association();
+      }
+
+      if(fromAssociation.isEmpty() && fromAssociation.equals(toAssociation)) {
+        return createCanonicalRelationshipName(getFromRoleName(), getToRoleName());
+      } else if(toAssociation.isEmpty()) {
+        return fromAssociation;
+      } else if(!toAssociation.equals(fromAssociation)) {
+        throw new AnnotationRuntimeException("Invalid associations for navigation properties '" +
+            this.toString() + "'");
+      }
+      return toAssociation;
+    }
+
+    public String getFromTypeName() {
+      if(isBiDirectional()) {
+        return extractEntityTypeName(toField.getDeclaringClass());
+      }
+      return extractEntityTypeName(fromField.getDeclaringClass());
+    }
+
+    public String getToTypeName() {
+      if(isBiDirectional()) {
+        return extractEntityTypeName(ClassHelper.getFieldType(toField));
+      }
+      return extractEntityTypeName(toField.getDeclaringClass());
+    }
+
+    @Override
+    public String toString() {
+      if(isBiDirectional()) {
+        return "AnnotatedNavInfo{biDirectional = true" +
+            ", toField=" + toField.getName() +
+            ", toNavigation=" + toNavigation.name() + '}';
+      }
+      return "AnnotatedNavInfo{" +
+          "fromField=" + fromField.getName() +
+          ", toField=" + toField.getName() +
+          ", fromNavigation=" + fromNavigation.name() +
+          ", toNavigation=" + toNavigation.name() + '}';
     }
   }
 
+
   public AnnotatedNavInfo getCommonNavigationInfo(final Class<?> sourceClass, final Class<?> targetClass) {
     List<Field> sourceFields = getAnnotatedFields(sourceClass, EdmNavigationProperty.class);
     List<Field> targetFields = getAnnotatedFields(targetClass, EdmNavigationProperty.class);
 
+    if(sourceClass == targetClass) {
+      // special case, actual handled as bi-directional
+      return getCommonNavigationInfoBiDirectional(sourceClass, targetClass);
+    }
+
     // first try via association name to get full navigation information
     for (Field sourceField : sourceFields) {
-      final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
-      final String sourceAssociation = extractRelationshipName(sourceNav, sourceField);
-      for (Field targetField : targetFields) {
-        final EdmNavigationProperty targetNav = targetField.getAnnotation(EdmNavigationProperty.class);
-        final String targetAssociation = extractRelationshipName(targetNav, targetField);
-        if (sourceAssociation.equals(targetAssociation)) {
-          return new AnnotatedNavInfo(sourceField, targetField, sourceNav, targetNav);
+      if(ClassHelper.getFieldType(sourceField) == targetClass) {
+        final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
+        final String sourceAssociation = extractRelationshipName(sourceNav, sourceField);
+        for (Field targetField : targetFields) {
+          if(ClassHelper.getFieldType(targetField) == sourceClass) {
+            final EdmNavigationProperty targetNav = targetField.getAnnotation(EdmNavigationProperty.class);
+            final String targetAssociation = extractRelationshipName(targetNav, targetField);
+            if (sourceAssociation.equals(targetAssociation)) {
+              return new AnnotatedNavInfo(sourceField, targetField, sourceNav, targetNav);
+            }
+          }
         }
       }
     }
 
-    // if nothing was found assume none bi-directinal navigation
+    // if nothing was found assume/guess none bi-directional navigation
+    return getCommonNavigationInfoBiDirectional(sourceClass, targetClass);
+  }
+
+  private AnnotatedNavInfo getCommonNavigationInfoBiDirectional(final Class<?> sourceClass,
+                                                                final Class<?> targetClass) {
+    List<Field> sourceFields = getAnnotatedFields(sourceClass, EdmNavigationProperty.class);
+
     String targetEntityTypeName = extractEntityTypeName(targetClass);
     for (Field sourceField : sourceFields) {
       final EdmNavigationProperty sourceNav = sourceField.getAnnotation(EdmNavigationProperty.class);
-      final String navTargetEntityName = extractEntitTypeName(sourceNav, sourceField);
+      final String navTargetEntityName = extractEntityTypeName(sourceNav, sourceField);
 
       if (navTargetEntityName.equals(targetEntityTypeName)) {
-        return new AnnotatedNavInfo(sourceField, null, sourceNav, null);
+        return new AnnotatedNavInfo(null, sourceField, null, sourceNav);
       }
     }
 
@@ -576,7 +667,6 @@
 
   /**
    * 
-   * @param instance
    * @param resultClass
    * @param annotation
    * @param inherited
diff --git a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java
index 24082ff..a5e9eb9 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/util/ClassHelper.java
@@ -23,6 +23,7 @@
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -180,6 +181,21 @@
     }
   }
 
+  /**
+   * Get the type of the field. For arrays and collections the type of the array or collection is returned.
+   *
+   * @param field field for which the type is extracted
+   * @return type of the field (also for arrays or collections)
+   */
+  public static Class<?> getFieldType(Field field) {
+    if(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType())) {
+      return (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+    } else {
+      return field.getType();
+    }
+  }
+
+
   public static Object getFieldValue(final Object instance, final Field field) {
     try {
       synchronized (field) {
diff --git a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java
index 44187d4..50a1520 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/edm/AnnotationEdmProviderTest.java
@@ -222,7 +222,7 @@
     assertEquals("Buildings", asBuildingRooms.getEnd1().getEntitySet());
     assertEquals("r_Building", asBuildingRooms.getEnd1().getRole());
     assertEquals("Rooms", asBuildingRooms.getEnd2().getEntitySet());
-    assertEquals("r_Room", asBuildingRooms.getEnd2().getRole());
+    assertEquals("r_Rooms", asBuildingRooms.getEnd2().getRole());
   }
 
   @Test
@@ -252,7 +252,7 @@
     assertEquals(6, entitySets.size());
 
     List<Association> associations = schema.getAssociations();
-    assertEquals(4, associations.size());
+    assertEquals(5, associations.size());
     for (Association association : associations) {
       assertNotNull(association.getName());
       validateAssociation(association);
@@ -265,22 +265,26 @@
 
   private void validateAssociation(final Association association) {
     String name = association.getName();
-    if (name.equals("r_Employee-r_Room")) {
+    if (name.equals("r_Employees-r_Room")) {
       validateAssociation(association,
           "r_Room", EdmMultiplicity.ONE, defaultFqn("Room"),
-          "r_Employee", EdmMultiplicity.MANY, defaultFqn("Employee"));
+          "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
     } else if (name.equals("BuildingRooms")) {
       validateAssociation(association,
           "r_Building", EdmMultiplicity.ONE, defaultFqn("Building"),
-          "r_Room", EdmMultiplicity.MANY, defaultFqn("Room"));
+          "r_Rooms", EdmMultiplicity.MANY, defaultFqn("Room"));
     } else if (name.equals("ManagerEmployees")) {
       validateAssociation(association,
           "r_Manager", EdmMultiplicity.ONE, defaultFqn("Manager"),
-          "r_Employee", EdmMultiplicity.MANY, defaultFqn("Employee"));
+          "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
     } else if (name.equals("TeamEmployees")) {
       validateAssociation(association,
           "r_Team", EdmMultiplicity.ONE, defaultFqn("Team"),
-          "r_Employee", EdmMultiplicity.MANY, defaultFqn("Employee"));
+          "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
+    } else if (name.equals("Team-r_SubTeam")) {
+      validateAssociation(association,
+          "Team", EdmMultiplicity.ONE, defaultFqn("Team"),
+          "r_SubTeam", EdmMultiplicity.ONE, defaultFqn("Team"));
     } else {
       fail("Got unknown association to validate with name '" + name + "'.");
     }
@@ -333,11 +337,11 @@
 
     for (NavigationProperty navigationProperty : employee.getNavigationProperties()) {
       if (navigationProperty.getName().equals("ne_Manager")) {
-        validateNavProperty(navigationProperty, "ManagerEmployees", "r_Employee", "r_Manager");
+        validateNavProperty(navigationProperty, "ManagerEmployees", "r_Employees", "r_Manager");
       } else if (navigationProperty.getName().equals("ne_Team")) {
-        validateNavProperty(navigationProperty, "TeamEmployees", "r_Employee", "r_Team");
+        validateNavProperty(navigationProperty, "TeamEmployees", "r_Employees", "r_Team");
       } else if (navigationProperty.getName().equals("ne_Room")) {
-        validateNavProperty(navigationProperty, "r_Employee-r_Room", "r_Employee", "r_Room");
+        validateNavProperty(navigationProperty, "r_Employees-r_Room", "r_Employees", "r_Room");
       } else {
         fail("Got unexpected navigation property with name '" + navigationProperty.getName() + "'.");
       }
@@ -376,9 +380,11 @@
     assertEquals(ModelSharedConstants.NAMESPACE_1, team.getBaseType().getNamespace());
 
     assertEquals(1, team.getProperties().size());
-    assertEquals(1, team.getNavigationProperties().size());
-    NavigationProperty navigationProperty = team.getNavigationProperties().get(0);
-    validateNavProperty(navigationProperty, "TeamEmployees", "r_Team", "r_Employee");
+    assertEquals(2, team.getNavigationProperties().size());
+    NavigationProperty navPropTeamEmployess = team.getNavigationProperties().get(0);
+    validateNavProperty(navPropTeamEmployess, "TeamEmployees", "r_Team", "r_Employees");
+    NavigationProperty navPropTeamTeam = team.getNavigationProperties().get(1);
+    validateNavProperty(navPropTeamTeam, "Team-r_SubTeam", "Team", "r_SubTeam");
   }
 
   @Test
@@ -459,9 +465,9 @@
 
     for (NavigationProperty navigationProperty : navigationProperties) {
       if (navigationProperty.getName().equals("nr_Employees")) {
-        validateNavProperty(navigationProperty, "r_Employee-r_Room", "r_Room", "r_Employee");
+        validateNavProperty(navigationProperty, "r_Employees-r_Room", "r_Room", "r_Employees");
       } else if (navigationProperty.getName().equals("nr_Building")) {
-        validateNavProperty(navigationProperty, "BuildingRooms", "r_Room", "r_Building");
+        validateNavProperty(navigationProperty, "BuildingRooms", "r_Rooms", "r_Building");
       } else {
         fail("Got unexpected navigation property with name '" + navigationProperty.getName() + "'.");
       }
diff --git a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java
index 8e5e794..23371a6 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/model/Team.java
@@ -38,6 +38,8 @@
   private Boolean isScrumTeam;
   @EdmNavigationProperty(name = "nt_Employees", association = "TeamEmployees", toMultiplicity = Multiplicity.MANY)
   private List<Employee> employees = new ArrayList<Employee>();
+  @EdmNavigationProperty
+  private Team subTeam;
 
   public Team() {
     super(-1, null);
@@ -63,6 +65,10 @@
     return employees;
   }
 
+  public Team getSubTeam() {
+    return subTeam;
+  }
+
   @Override
   public int hashCode() {
     return id;
diff --git a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java
index 18d7df2..478a1b7 100644
--- a/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/test/java/org/apache/olingo/odata2/annotation/processor/core/util/AnnotationHelperTest.java
@@ -17,6 +17,7 @@
 
 import java.lang.reflect.Field;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import junit.framework.Assert;
@@ -160,26 +161,36 @@
   }
 
   @Test
-  public void extractEntitTypeNameViaNavigation() throws Exception {
+  public void extractEntityTypeNameViaNavigation() throws Exception {
     Field field = NavigationAnnotated.class.getDeclaredField("navigationPropertySimpleEntity");
     EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
 
-    String name = annotationHelper.extractEntitTypeName(enp, SimpleEntity.class);
+    String name = annotationHelper.extractEntityTypeName(enp, SimpleEntity.class);
 
     Assert.assertEquals("SimpleEntity", name);
   }
 
   @Test
-  public void extractEntitTypeNameViaNavigationField() throws Exception {
+  public void extractEntityTypeNameViaNavigationField() throws Exception {
     Field field = NavigationAnnotated.class.getDeclaredField("navigationPropertyDefault");
     EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
 
-    String name = annotationHelper.extractEntitTypeName(enp, field);
+    String name = annotationHelper.extractEntityTypeName(enp, field);
 
     Assert.assertEquals("SimpleEntity", name);
   }
 
   @Test
+  public void selfReferencedEntityTypeNameViaNavigationField() throws Exception {
+    Field field = NavigationAnnotated.class.getDeclaredField("selfReferencedNavigation");
+    EdmNavigationProperty enp = field.getAnnotation(EdmNavigationProperty.class);
+
+    String name = annotationHelper.extractToRoleName(enp, field);
+
+    Assert.assertEquals("r_SelfReferencedNavigation", name);
+  }
+
+  @Test
   public void getFieldTypeForPropertyNullInstance() throws Exception {
     Object result = annotationHelper.getFieldTypeForProperty(null, "");
     Assert.assertNull(result);
@@ -247,11 +258,14 @@
     }
   }
 
+  @EdmEntityType
   private class NavigationAnnotated {
     @EdmNavigationProperty(toType = SimpleEntity.class)
     SimpleEntity navigationPropertySimpleEntity;
     @EdmNavigationProperty
     SimpleEntity navigationPropertyDefault;
+    @EdmNavigationProperty
+    List<NavigationAnnotated> selfReferencedNavigation;
   }
 
   private class ConversionProperty {
diff --git a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java
index 0977a83..65f5585 100644
--- a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java
+++ b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/AnnotationRefServiceFactory.java
@@ -145,7 +145,9 @@
     teamDs.create(createTeam("Team Beta", false));
     teamDs.create(createTeam("Team Gamma", false));
     teamDs.create(createTeam("Team Omega", true));
-    teamDs.create(createTeam("Team Zeta", true));
+    Team subTeam = createTeam("SubTeamOne", false);
+    teamDs.create(subTeam);
+    teamDs.create(createTeam("Team Zeta", true, subTeam));
 
     InMemoryDataStore<Building> buildingsDs = getDataStore(Building.class);
     Building redBuilding = createBuilding("Red Building");
@@ -198,9 +200,14 @@
   }
 
   private static Team createTeam(final String teamName, final boolean isScrumTeam) {
+    return createTeam(teamName, isScrumTeam, null);
+  }
+
+  private static Team createTeam(final String teamName, final boolean isScrumTeam, Team subTeam) {
     Team team = new Team();
     team.setName(teamName);
     team.setScrumTeam(isScrumTeam);
+    team.setSubTeam(subTeam);
     return team;
   }
 
diff --git a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java
index 7f48f66..9e3d38c 100644
--- a/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java
+++ b/odata2-annotation-processor/annotation-processor-ref/src/main/java/org/apache/olingo/odata2/annotation/processor/ref/model/Team.java
@@ -38,6 +38,8 @@
   private Boolean isScrumTeam;
   @EdmNavigationProperty(name = "nt_Employees", association = "TeamEmployees", toMultiplicity = Multiplicity.MANY)
   private List<Employee> employees = new ArrayList<Employee>();
+  @EdmNavigationProperty
+  private Team subTeam;
 
   public Boolean isScrumTeam() {
     return isScrumTeam;
@@ -55,6 +57,14 @@
     return employees;
   }
 
+  public void setSubTeam(Team subTeam) {
+    this.subTeam = subTeam;
+  }
+
+  public Team getSubTeam() {
+    return subTeam;
+  }
+
   @Override
   public int hashCode() {
     return super.hashCode();
diff --git a/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java b/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java
index 5114594..4818327 100644
--- a/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java
+++ b/odata2-annotation-processor/annotation-processor-ref/src/test/java/org/apache/olingo/odata2/annotation/processor/ref/MetadataTest.java
@@ -101,17 +101,17 @@
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Employee' and" +
             " @m:HasStream='true']/edm:NavigationProperty[@Name='ne_Manager' and " +
-            "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Employee' and @ToRole='r_Manager']",
+            "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Employees' and @ToRole='r_Manager']",
         payload);
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Employee' and" +
             " @m:HasStream='true']/edm:NavigationProperty[@Name='ne_Team' and " +
-            "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Employee' and @ToRole='r_Team']",
+            "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Employees' and @ToRole='r_Team']",
         payload);
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Employee' and " +
             "@m:HasStream='true']/edm:NavigationProperty[@Name='ne_Room' and " +
-            "@Relationship='RefScenario.r_Employee-r_Room' and @FromRole='r_Employee' and @ToRole='r_Room']",
+            "@Relationship='RefScenario.r_Employees-r_Room' and @FromRole='r_Employees' and @ToRole='r_Room']",
         payload);
 
     // Team
@@ -126,7 +126,12 @@
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Team' and " +
             "@BaseType='RefScenario.Base']/edm:NavigationProperty[@Name='nt_Employees' and " +
-            "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Team' and @ToRole='r_Employee']",
+            "@Relationship='RefScenario.TeamEmployees' and @FromRole='r_Team' and @ToRole='r_Employees']",
+        payload);
+    assertXpathExists(
+        "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Team' and " +
+            "@BaseType='RefScenario.Base']/edm:NavigationProperty[@Name='SubTeam' and " +
+            "@Relationship='RefScenario.Team-r_SubTeam' and @FromRole='Team' and @ToRole='r_SubTeam']",
         payload);
 
     // Room
@@ -144,12 +149,12 @@
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Room' and" +
             " @BaseType='RefScenario.Base']/edm:NavigationProperty[@Name='nr_Employees' and " +
-            "@Relationship='RefScenario.r_Employee-r_Room' and @FromRole='r_Room' and @ToRole='r_Employee']",
+            "@Relationship='RefScenario.r_Employees-r_Room' and @FromRole='r_Room' and @ToRole='r_Employees']",
         payload);
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Room' and " +
             "@BaseType='RefScenario.Base']/edm:NavigationProperty[@Name='nr_Building' and " +
-            "@Relationship='RefScenario.BuildingRooms' and @FromRole='r_Room' and @ToRole='r_Building']",
+            "@Relationship='RefScenario.BuildingRooms' and @FromRole='r_Rooms' and @ToRole='r_Building']",
         payload);
 
     // Manager
@@ -157,7 +162,7 @@
             "@BaseType='RefScenario.Employee']", payload);
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Manager' and " +
             "@BaseType='RefScenario.Employee']/edm:NavigationProperty[@Name='nm_Employees' and " +
-            "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Manager' and @ToRole='r_Employee']",
+            "@Relationship='RefScenario.ManagerEmployees' and @FromRole='r_Manager' and @ToRole='r_Employees']",
         payload);
 
     // Building
@@ -174,7 +179,7 @@
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Building']" +
                 "/edm:NavigationProperty[@Name='nb_Rooms' and @Relationship='RefScenario.BuildingRooms' " +
-                "and @FromRole='r_Building' and @ToRole='r_Room']", payload);
+                "and @FromRole='r_Building' and @ToRole='r_Rooms']", payload);
 
     // Base
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityType[@Name='Base' and @Abstract='true']",
@@ -219,22 +224,29 @@
     // ManagerEmployees
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='ManagerEmployees']", payload);
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='ManagerEmployees']" +
-            "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employee']", payload);
+            "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employees']", payload);
     assertXpathExists( "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='ManagerEmployees']" +
             "/edm:End[@Type='RefScenario.Manager' and @Multiplicity='1' and @Role='r_Manager']", payload);
 
     // TeamEmployees
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='TeamEmployees']", payload);
     assertXpathExists( "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='TeamEmployees']" +
-            "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employee']", payload);
+            "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employees']", payload);
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='TeamEmployees']" +
             "/edm:End[@Type='RefScenario.Team' and @Multiplicity='1' and @Role='r_Team']", payload);
 
+    // Team-r_SubTeam
+    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='Team-r_SubTeam']", payload);
+    assertXpathExists( "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='Team-r_SubTeam']" +
+        "/edm:End[@Type='RefScenario.Team' and @Multiplicity='1' and @Role='Team']", payload);
+    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='Team-r_SubTeam']" +
+        "/edm:End[@Type='RefScenario.Team' and @Multiplicity='1' and @Role='r_SubTeam']", payload);
+
     // RoomEmployees
-    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employee-r_Room']", payload);
-    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employee-r_Room']" +
-            "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employee']", payload);
-    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employee-r_Room']" +
+    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employees-r_Room']", payload);
+    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employees-r_Room']" +
+            "/edm:End[@Type='RefScenario.Employee' and @Multiplicity='*' and @Role='r_Employees']", payload);
+    assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='r_Employees-r_Room']" +
             "/edm:End[@Type='RefScenario.Room' and @Multiplicity='1' and @Role='r_Room']", payload);
 
     // BuildingRooms
@@ -242,7 +254,7 @@
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='BuildingRooms']" +
             "/edm:End[@Type='RefScenario.Building' and @Multiplicity='1' and @Role='r_Building']", payload);
     assertXpathExists("/edmx:Edmx/edmx:DataServices/edm:Schema/edm:Association[@Name='BuildingRooms']" +
-            "/edm:End[@Type='RefScenario.Room' and @Multiplicity='*' and @Role='r_Room']", payload);
+            "/edm:End[@Type='RefScenario.Room' and @Multiplicity='*' and @Role='r_Rooms']", payload);
   }
 
   @Test
@@ -291,7 +303,7 @@
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
             "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='ManagerEmployees' and " +
-            "@Association='RefScenario.ManagerEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employee']",
+            "@Association='RefScenario.ManagerEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employees']",
         payload);
 
     assertXpathExists(
@@ -307,23 +319,33 @@
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
             "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='TeamEmployees' and " +
-            "@Association='RefScenario.TeamEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employee']",
+            "@Association='RefScenario.TeamEmployees']/edm:End[@EntitySet='Employees' and @Role='r_Employees']",
+        payload);
+    assertXpathExists(
+        "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
+            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='Team-r_SubTeam' and " +
+            "@Association='RefScenario.Team-r_SubTeam']/edm:End[@EntitySet='Teams' and @Role='Team']",
+        payload);
+    assertXpathExists(
+        "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
+            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='Team-r_SubTeam' and " +
+            "@Association='RefScenario.Team-r_SubTeam']/edm:End[@EntitySet='Teams' and @Role='r_SubTeam']",
         payload);
 
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
-            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employee-r_Room' and " +
-            "@Association='RefScenario.r_Employee-r_Room']",
+            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employees-r_Room' and " +
+            "@Association='RefScenario.r_Employees-r_Room']",
         payload);
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
-            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employee-r_Room' and " +
-            "@Association='RefScenario.r_Employee-r_Room']/edm:End[@EntitySet='Rooms' and @Role='r_Room']",
+            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employees-r_Room' and " +
+            "@Association='RefScenario.r_Employees-r_Room']/edm:End[@EntitySet='Rooms' and @Role='r_Room']",
         payload);
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
-            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employee-r_Room' and " +
-            "@Association='RefScenario.r_Employee-r_Room']/edm:End[@EntitySet='Employees' and @Role='r_Employee']",
+            "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='r_Employees-r_Room' and " +
+            "@Association='RefScenario.r_Employees-r_Room']/edm:End[@EntitySet='Employees' and @Role='r_Employees']",
         payload);
 
     assertXpathExists(
@@ -339,7 +361,7 @@
     assertXpathExists(
         "/edmx:Edmx/edmx:DataServices/edm:Schema/edm:EntityContainer[@Name='DefaultContainer' and " +
             "@m:IsDefaultEntityContainer='true']/edm:AssociationSet[@Name='BuildingRooms' and " +
-            "@Association='RefScenario.BuildingRooms']/edm:End[@EntitySet='Rooms' and @Role='r_Room']",
+            "@Association='RefScenario.BuildingRooms']/edm:End[@EntitySet='Rooms' and @Role='r_Rooms']",
         payload);
   }
 }
diff --git a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/ODataJPAServiceFactory.java b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/ODataJPAServiceFactory.java
index 82aa68d..709a048 100644
--- a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/ODataJPAServiceFactory.java
+++ b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/ODataJPAServiceFactory.java
@@ -63,8 +63,8 @@
  *    <param-value>org.apache.olingo.odata2.core.rest.ODataApplication</param-value>
  *  </init-param>
  *  <init-param>
- *    <param-name>org.apache.olingo.odata2.processor.factory</param-name>
- *    <param-value>foo.bar.sample.processor.SampleProcessorFactory</param-value>
+ *    <param-name>org.apache.olingo.odata2.service.factory</param-name>
+ *    <param-value>foo.bar.sample.service.SampleProcessorFactory</param-value>
  *  </init-param>
  *  <init-param>
  *    <param-name>org.apache.olingo.odata2.path.split</param-name>
diff --git a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/exception/ODataJPAModelException.java b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/exception/ODataJPAModelException.java
index 9f18f37..0ddb284 100644
--- a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/exception/ODataJPAModelException.java
+++ b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/exception/ODataJPAModelException.java
@@ -60,6 +60,8 @@
       "INNER_EXCEPTION");
   public static final MessageReference FUNC_PARAM_NAME_EXP = createMessageReference(ODataJPAModelException.class,
       "FUNC_PARAM_NAME_EXP");
+  public static final MessageReference REF_ATTRIBUTE_NOT_FOUND = createMessageReference(ODataJPAModelException.class,
+      "REF_ATTRIBUTE_NOT_FOUND");
 
   private ODataJPAModelException(final String localizedMessage, final Throwable e, final MessageReference msgRef) {
     super(localizedMessage, e, msgRef);
diff --git a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/factory/JPAAccessFactory.java b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/factory/JPAAccessFactory.java
index 20f965e..37e4bbb 100644
--- a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/factory/JPAAccessFactory.java
+++ b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/factory/JPAAccessFactory.java
@@ -21,6 +21,7 @@
 import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext;
 import org.apache.olingo.odata2.jpa.processor.api.access.JPAEdmMappingModelAccess;
 import org.apache.olingo.odata2.jpa.processor.api.access.JPAProcessor;
+import org.apache.olingo.odata2.jpa.processor.api.model.JPAEdmMapping;
 import org.apache.olingo.odata2.jpa.processor.api.model.JPAEdmModelView;
 
 /**
@@ -66,4 +67,10 @@
    * @return an instance of type {@link org.apache.olingo.odata2.jpa.processor.api.access.JPAEdmMappingModelAccess}
    */
   public JPAEdmMappingModelAccess getJPAEdmMappingModelAccess(ODataJPAContext oDataJPAContext);
+
+  /**
+   * The method instantiates a JPAEdmMapping instance.
+   * @return an instance of type {@link org.apache.olingo.odata2.jpa.processor.api.model.JPAEdmMapping}
+   */
+  public JPAEdmMapping getJPAEdmMappingInstance();
 }
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAProcessorDefault.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAProcessorDefault.java
index 079aecc..6b1f40b 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAProcessorDefault.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAProcessorDefault.java
@@ -58,9 +58,9 @@
   @Override
   public ODataResponse readEntitySet(final GetEntitySetUriInfo uriParserResultView, final String contentType)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     List<Object> jpaEntities = jpaProcessor.process(uriParserResultView);
 
     ODataResponse oDataResponse =
@@ -74,9 +74,9 @@
   @Override
   public ODataResponse readEntity(final GetEntityUriInfo uriParserResultView, final String contentType)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     Object jpaEntity = jpaProcessor.process(uriParserResultView);
 
     ODataResponse oDataResponse =
@@ -88,9 +88,9 @@
   @Override
   public ODataResponse countEntitySet(final GetEntitySetCountUriInfo uriParserResultView, final String contentType)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     long jpaEntityCount = jpaProcessor.process(uriParserResultView);
 
     ODataResponse oDataResponse = responseBuilder.build(jpaEntityCount);
@@ -101,9 +101,9 @@
   @Override
   public ODataResponse existsEntity(final GetEntityCountUriInfo uriInfo, final String contentType)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     long jpaEntityCount = jpaProcessor.process(uriInfo);
 
     ODataResponse oDataResponse = responseBuilder.build(jpaEntityCount);
@@ -114,9 +114,9 @@
   @Override
   public ODataResponse createEntity(final PostUriInfo uriParserResultView, final InputStream content,
       final String requestContentType, final String contentType) throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     Object createdJpaEntity = jpaProcessor.process(uriParserResultView, content, requestContentType);
 
     ODataResponse oDataResponse =
@@ -128,9 +128,9 @@
   @Override
   public ODataResponse updateEntity(final PutMergePatchUriInfo uriParserResultView, final InputStream content,
       final String requestContentType, final boolean merge, final String contentType) throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     Object jpaEntity = jpaProcessor.process(uriParserResultView, content, requestContentType);
 
     ODataResponse oDataResponse = responseBuilder.build(uriParserResultView, jpaEntity);
@@ -141,9 +141,9 @@
   @Override
   public ODataResponse deleteEntity(final DeleteUriInfo uriParserResultView, final String contentType)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     Object deletedObj = jpaProcessor.process(uriParserResultView, contentType);
 
     ODataResponse oDataResponse = responseBuilder.build(uriParserResultView, deletedObj);
@@ -153,9 +153,9 @@
   @Override
   public ODataResponse executeFunctionImport(final GetFunctionImportUriInfo uriParserResultView,
       final String contentType) throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     List<Object> resultEntity = jpaProcessor.process(uriParserResultView);
 
     ODataResponse oDataResponse =
@@ -167,13 +167,13 @@
   @Override
   public ODataResponse executeFunctionImportValue(final GetFunctionImportUriInfo uriParserResultView,
       final String contentType) throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     List<Object> result = jpaProcessor.process(uriParserResultView);
 
     ODataResponse oDataResponse =
-        responseBuilder.build(uriParserResultView, result, contentType);
+        responseBuilder.build(uriParserResultView, result.get(0));
 
     return oDataResponse;
   }
@@ -181,9 +181,9 @@
   @Override
   public ODataResponse readEntityLink(final GetEntityLinkUriInfo uriParserResultView, final String contentType)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     Object jpaEntity = jpaProcessor.process(uriParserResultView);
 
     ODataResponse oDataResponse =
@@ -197,7 +197,7 @@
       throws ODataException {
 
     oDataJPAContext.setODataContext(getContext());
-    
+
     List<Object> jpaEntity = jpaProcessor.process(uriParserResultView);
 
     ODataResponse oDataResponse =
@@ -211,7 +211,7 @@
       final String requestContentType, final String contentType) throws ODataException {
 
     oDataJPAContext.setODataContext(getContext());
-    
+
     jpaProcessor.process(uriParserResultView, content, requestContentType, contentType);
 
     return ODataResponse.newBuilder().build();
@@ -222,7 +222,7 @@
       final String requestContentType, final String contentType) throws ODataException {
 
     oDataJPAContext.setODataContext(getContext());
-    
+
     jpaProcessor.process(uriParserResultView, content, requestContentType, contentType);
 
     return ODataResponse.newBuilder().build();
@@ -231,9 +231,9 @@
   @Override
   public ODataResponse deleteEntityLink(final DeleteUriInfo uriParserResultView, final String contentType)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     jpaProcessor.process(uriParserResultView, contentType);
     return ODataResponse.newBuilder().build();
 
@@ -242,9 +242,9 @@
   @Override
   public ODataResponse executeBatch(final BatchHandler handler, final String contentType, final InputStream content)
       throws ODataException {
-    
+
     oDataJPAContext.setODataContext(getContext());
-    
+
     ODataResponse batchResponse;
     List<BatchResponsePart> batchResponseParts = new ArrayList<BatchResponsePart>();
     PathInfo pathInfo = getContext().getPathInfo();
@@ -282,7 +282,7 @@
 
       return BatchResponsePart.responses(responses).changeSet(true).build();
     } catch (Exception e) {
-      
+
       List<ODataResponse> errorResponses = new ArrayList<ODataResponse>(1);
       errorResponses.add(ODataResponse.entity(e).status(HttpStatusCodes.INTERNAL_SERVER_ERROR).build());
       return BatchResponsePart.responses(errorResponses).changeSet(false).build();
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAResponseBuilderDefault.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAResponseBuilderDefault.java
index fc0653f..edec5fb 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAResponseBuilderDefault.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/ODataJPAResponseBuilderDefault.java
@@ -267,9 +267,12 @@
 
       if (result != null) {
         ODataResponse response = null;
-
-        final String value = type.valueToString(result, EdmLiteralKind.DEFAULT, null);
-        response = EntityProvider.writeText(value);
+        if (type.getDefaultType().equals(byte[].class)) {
+          response = EntityProvider.writeBinary("application/octet-stream", (byte[]) result);
+        } else {
+          final String value = type.valueToString(result, EdmLiteralKind.DEFAULT, null);
+          response = EntityProvider.writeText(value);
+        }
 
         return ODataResponse.fromResponse(response).build();
       } else {
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntity.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntity.java
index 847e171..e0135b2 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntity.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntity.java
@@ -336,6 +336,9 @@
         } else if (parameterType.equals(Character.class)) {
           Character c = Character.valueOf(((String) entityPropertyValue).charAt(0));
           method.invoke(entity, c);
+        } else if (parameterType.isEnum()) {
+          Enum e = Enum.valueOf((Class<Enum>) parameterType, (String) entityPropertyValue);
+          method.invoke(entity, e);
         }
       } else if (parameterType.equals(Blob.class)) {
         if (onJPAWriteContent == null) {
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPAEdmNameBuilder.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPAEdmNameBuilder.java
index 939ca88..352016e 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPAEdmNameBuilder.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPAEdmNameBuilder.java
@@ -26,6 +26,7 @@
 import javax.persistence.metamodel.ManagedType;
 import javax.persistence.metamodel.PluralAttribute;
 
+import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
 import org.apache.olingo.odata2.api.edm.FullQualifiedName;
 import org.apache.olingo.odata2.api.edm.provider.Association;
 import org.apache.olingo.odata2.api.edm.provider.AssociationSet;
@@ -142,12 +143,15 @@
                 jpaAttributeName);
       }
     }
+    if (isForeignKey == true) {
+      joinColumnNames = view.getJPAJoinColumns().get(view.getJPAJoinColumns().size() - 1);
+    }
+
     if (skipDefaultNaming == false && propertyName == null) {
       propertyName = Character.toUpperCase(jpaAttributeName.charAt(0)) + jpaAttributeName.substring(1);
     } else if (propertyName == null) {
       propertyName = jpaAttributeName;
-      if (isForeignKey == true) {
-        joinColumnNames = view.getJPAJoinColumns().get(view.getJPAJoinColumns().size() - 1);
+      if (isForeignKey) {
         propertyName = mappingModelAccess.mapJPAAttribute(view.getJPAEdmEntityTypeView().getJPAEntityType().getName(),
             joinColumnNames[0]);
         if (propertyName == null) {
@@ -379,19 +383,6 @@
 
   }
 
-  private static String buildNamespace(final JPAEdmBaseView view) {
-    JPAEdmMappingModelAccess mappingModelAccess = view.getJPAEdmMappingModelAccess();
-    String namespace = null;
-    if (mappingModelAccess != null && mappingModelAccess.isMappingModelExists()) {
-      namespace = mappingModelAccess.mapJPAPersistenceUnit(view.getpUnitName());
-    }
-    if (namespace == null) {
-      namespace = view.getpUnitName();
-    }
-
-    return namespace;
-  }
-
   /*
    * ************************************************************************
    * EDM Association Name - RULES
@@ -408,16 +399,23 @@
     String end1Name = association.getEnd1().getType().getName();
     String end2Name = association.getEnd2().getType().getName();
 
-    if (end1Name.compareToIgnoreCase(end2Name) > 0) {
-      associationName = end2Name + UNDERSCORE + end1Name;
-    } else {
+    if (end1Name.equals(end2Name)) {
       associationName = end1Name + UNDERSCORE + end2Name;
+      associationName =
+          associationName + UNDERSCORE + multiplicityToString(association.getEnd1().getMultiplicity()) + UNDERSCORE
+              + multiplicityToString(association.getEnd2().getMultiplicity()) + Integer.toString(count);
+    } else {
+      if (end1Name.compareToIgnoreCase(end2Name) > 0) {
+        associationName = end2Name + UNDERSCORE + end1Name;
+      } else {
+        associationName = end1Name + UNDERSCORE + end2Name;
+      }
+      if (count > 1) {
+        associationName = associationName + Integer.toString(count - 1);
+      }
     }
-    if (count > 1) {
-      associationName = associationName + Integer.toString(count - 1);
-    }
-    association.setName(associationName);
 
+    association.setName(associationName);
   }
 
   /*
@@ -506,11 +504,21 @@
     // Condition for self join
     if (associationEndTypeOne.getName().equals(associationEndTypeTwo.getName())) {
       if (jpaAttribute.isCollection()) {
-        navProp.setFromRole(association.getEnd2().getRole());
-        navProp.setToRole(association.getEnd1().getRole());
+        if (association.getEnd2().getMultiplicity().equals(EdmMultiplicity.MANY)) {
+          navProp.setToRole(association.getEnd2().getRole());
+          navProp.setFromRole(association.getEnd1().getRole());
+        } else {
+          navProp.setToRole(association.getEnd1().getRole());
+          navProp.setFromRole(association.getEnd2().getRole());
+        }
       } else {
-        navProp.setToRole(association.getEnd2().getRole());
-        navProp.setFromRole(association.getEnd1().getRole());        
+        if (association.getEnd2().getMultiplicity().equals(EdmMultiplicity.ONE)) {
+          navProp.setToRole(association.getEnd2().getRole());
+          navProp.setFromRole(association.getEnd1().getRole());
+        } else {
+          navProp.setToRole(association.getEnd1().getRole());
+          navProp.setFromRole(association.getEnd2().getRole());
+        }
       }
     } else if (toName.equals(associationEndTypeOne.getName())) {
       navProp.setFromRole(association.getEnd2().getRole());
@@ -522,4 +530,31 @@
     }
   }
 
+  private static String multiplicityToString(EdmMultiplicity multiplicity) {
+    switch (multiplicity) {
+    case MANY:
+      return "Many";
+    case ONE:
+      return "One";
+    case ZERO_TO_ONE:
+      return "ZeroToOne";
+    default:
+      break;
+    }
+    return "";
+  }
+
+  private static String buildNamespace(final JPAEdmBaseView view) {
+    JPAEdmMappingModelAccess mappingModelAccess = view.getJPAEdmMappingModelAccess();
+    String namespace = null;
+    if (mappingModelAccess != null && mappingModelAccess.isMappingModelExists()) {
+      namespace = mappingModelAccess.mapJPAPersistenceUnit(view.getpUnitName());
+    }
+    if (namespace == null) {
+      namespace = view.getpUnitName();
+    }
+
+    return namespace;
+  }
+
 }
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertor.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertor.java
index 10a6d73..af8508f 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertor.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertor.java
@@ -105,7 +105,10 @@
       return EdmSimpleTypeKind.Binary;
     } else if (jpaType.equals(Clob.class) && isBlob(currentAttribute)) {
       return EdmSimpleTypeKind.String;
+    } else if (jpaType.isEnum()) {
+      return EdmSimpleTypeKind.String;
     }
+
     throw ODataJPAModelException.throwException(ODataJPAModelException.TYPE_NOT_SUPPORTED
         .addContent(jpaType.toString()), null);
   }
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/factory/ODataJPAFactoryImpl.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/factory/ODataJPAFactoryImpl.java
index 789afde..ce08757 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/factory/ODataJPAFactoryImpl.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/factory/ODataJPAFactoryImpl.java
@@ -36,6 +36,7 @@
 import org.apache.olingo.odata2.jpa.processor.api.jpql.JPQLContextType;
 import org.apache.olingo.odata2.jpa.processor.api.jpql.JPQLContextView;
 import org.apache.olingo.odata2.jpa.processor.api.jpql.JPQLStatement.JPQLStatementBuilder;
+import org.apache.olingo.odata2.jpa.processor.api.model.JPAEdmMapping;
 import org.apache.olingo.odata2.jpa.processor.api.model.JPAEdmModelView;
 import org.apache.olingo.odata2.jpa.processor.core.ODataJPAContextImpl;
 import org.apache.olingo.odata2.jpa.processor.core.ODataJPAProcessorDefault;
@@ -53,6 +54,7 @@
 import org.apache.olingo.odata2.jpa.processor.core.jpql.JPQLSelectSingleContext;
 import org.apache.olingo.odata2.jpa.processor.core.jpql.JPQLSelectSingleStatementBuilder;
 import org.apache.olingo.odata2.jpa.processor.core.jpql.JPQLSelectStatementBuilder;
+import org.apache.olingo.odata2.jpa.processor.core.model.JPAEdmMappingImpl;
 import org.apache.olingo.odata2.jpa.processor.core.model.JPAEdmModel;
 
 public class ODataJPAFactoryImpl extends ODataJPAFactory {
@@ -240,5 +242,10 @@
       return mappingModelAccess;
     }
 
+    @Override
+    public JPAEdmMapping getJPAEdmMappingInstance() {
+      return new JPAEdmMappingImpl();
+    }
+
   }
 }
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmAssociation.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmAssociation.java
index 9f35c3a..889047c 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmAssociation.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmAssociation.java
@@ -217,8 +217,6 @@
   @Override
   public int getNumberOfAssociationsWithSimilarEndPoints(final JPAEdmAssociationEndView view) {
     int count = 0;
-    AssociationEnd currentAssociationEnd1 = view.getEdmAssociationEnd1();
-    AssociationEnd currentAssociationEnd2 = view.getEdmAssociationEnd2();
     AssociationEnd end1 = null;
     AssociationEnd end2 = null;
     for (String key : associationMap.keySet()) {
@@ -226,10 +224,7 @@
       if (association != null) {
         end1 = association.getEnd1();
         end2 = association.getEnd2();
-        if ((end1.getType().equals(currentAssociationEnd1.getType()) && end2.getType().equals(
-            currentAssociationEnd2.getType()))
-            || (end1.getType().equals(currentAssociationEnd2.getType()) && end2.getType().equals(
-                currentAssociationEnd1.getType()))) {
+        if (view.compare(end1, end2)) {
           count++;
         }
       }
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmEntityType.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmEntityType.java
index eea7254..fd18166 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmEntityType.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmEntityType.java
@@ -113,15 +113,13 @@
         JPAEdmMapping jpaEdmMapping = (JPAEdmMapping) currentEdmEntityType.getMapping();
         EntityListeners entityListners = currentJPAEntityType.getJavaType().getAnnotation(EntityListeners.class);
         if (entityListners != null) {
-          for (Class<? extends ODataJPATombstoneEntityListener> entityListner : entityListners.value())
-          {
-            try {
-              jpaEdmMapping.setODataJPATombstoneEntityListener(entityListner);
+          for (Class<EntityListeners> entityListner : entityListners.value()) {
+            if (ODataJPATombstoneEntityListener.class.isAssignableFrom(entityListner)) {
+              jpaEdmMapping
+                  .setODataJPATombstoneEntityListener((Class<? extends ODataJPATombstoneEntityListener>)
+                  (Object) entityListner);
               break;
-            } catch (ClassCastException e) {
-              continue;
             }
-
           }
         }
         JPAEdmPropertyView propertyView = new JPAEdmProperty(schemaView);
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmProperty.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmProperty.java
index 33f0c93..fbb8f96 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmProperty.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmProperty.java
@@ -276,7 +276,10 @@
         case ONE_TO_ONE:
         case MANY_TO_ONE:
 
-          addForeignKey(currentAttribute);
+          if (attributeType.equals(PersistentAttributeType.MANY_TO_ONE)
+              || attributeType.equals(PersistentAttributeType.ONE_TO_ONE)) {
+            addForeignKey(currentAttribute);
+          }
 
           JPAEdmAssociationEndView associationEndView = new JPAEdmAssociationEnd(entityTypeView, JPAEdmProperty.this);
           associationEndView.getBuilder().build();
@@ -289,14 +292,17 @@
             associationView.addJPAEdmAssociationView(associationViewLocal, associationEndView);
           }
 
-          JPAEdmReferentialConstraintView refConstraintView = new JPAEdmReferentialConstraint(
-              associationView, entityTypeView, JPAEdmProperty.this);
-          refConstraintView.getBuilder().build();
+          if (attributeType.equals(PersistentAttributeType.MANY_TO_ONE)
+              || attributeType.equals(PersistentAttributeType.ONE_TO_ONE)) {
 
-          if (refConstraintView.isExists()) {
-            associationView.addJPAEdmRefConstraintView(refConstraintView);
+            JPAEdmReferentialConstraintView refConstraintView = new JPAEdmReferentialConstraint(
+                associationView, entityTypeView, JPAEdmProperty.this);
+            refConstraintView.getBuilder().build();
+
+            if (refConstraintView.isExists()) {
+              associationView.addJPAEdmRefConstraintView(refConstraintView);
+            }
           }
-
           if (navigationPropertyView == null) {
             navigationPropertyView = new JPAEdmNavigationProperty(schemaView);
           }
@@ -372,7 +378,14 @@
       String[] name = { null, null };
       name[0] = joinColumn.name().equals("") == true ? jpaAttribute.getName() : joinColumn.name();
 
-      EntityType<?> referencedEntityType = metaModel.entity(jpaAttribute.getJavaType());
+      EntityType<?> referencedEntityType = null;
+      if (jpaAttribute.isCollection()) {
+        referencedEntityType =
+            metaModel.entity(((PluralAttribute<?, ?, ?>) currentAttribute).getElementType().getJavaType());
+      } else {
+        referencedEntityType = metaModel.entity(jpaAttribute.getJavaType());
+      }
+
       if (joinColumn.referencedColumnName().equals("")) {
         for (Attribute<?, ?> referencedAttribute : referencedEntityType.getAttributes()) {
           if (referencedAttribute.getPersistentAttributeType() == PersistentAttributeType.BASIC &&
@@ -407,6 +420,10 @@
         }
       }
 
+      if (currentRefAttribute == null) {
+        throw ODataJPAModelException.throwException(ODataJPAModelException.REF_ATTRIBUTE_NOT_FOUND
+            .addContent(referencedEntityType.getName()), null);
+      }
       if (joinColumn.insertable() && joinColumn.updatable()) {
         currentSimpleProperty = new SimpleProperty();
         properties.add(buildSimpleProperty(currentRefAttribute, currentSimpleProperty, true));
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmReferentialConstraintRole.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmReferentialConstraintRole.java
index 2a5045a..9c3c89e 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmReferentialConstraintRole.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmReferentialConstraintRole.java
@@ -141,6 +141,7 @@
 
     private void buildRole() throws SecurityException, NoSuchFieldException {
 
+      int index = 0;
       if (currentRole == null) {
         currentRole = new ReferentialConstraintRole();
         String jpaAttributeType = null;
@@ -157,6 +158,7 @@
             jpaAttributeType = type.toString().substring(lastIndexOfDot + 1);
           }
           edmEntityType = entityTypeView.searchEdmEntityType(jpaAttributeType);
+          index = 1;
         } else if (roleType == RoleType.DEPENDENT) {
           edmEntityType =
               entityTypeView.searchEdmEntityType(jpaAttribute.getDeclaringType().getJavaType().getSimpleName());
@@ -167,10 +169,8 @@
           for (String[] columnName : jpaColumnNames) {
             for (Property property : edmEntityType.getProperties()) {
               jpaColumnName = ((JPAEdmMapping) property.getMapping()).getJPAColumnName();
-              if (columnName[0].equals(jpaColumnName) ||
-                  columnName[0].equals(property.getName()) ||
-                  columnName[1].equals(jpaColumnName) ||
-                  columnName[1].equals(property.getName())) {
+              if (columnName[index].equals(jpaColumnName) ||
+                  columnName[index].equals(property.getName())) {
                 PropertyRef propertyRef = new PropertyRef();
                 propertyRef.setName(property.getName());
                 propertyRefs.add(propertyRef);
diff --git a/odata2-jpa-processor/jpa-core/src/main/resources/jpaprocessor_msg.properties b/odata2-jpa-processor/jpa-core/src/main/resources/jpaprocessor_msg.properties
index 29c1006..7b4bb95 100644
--- a/odata2-jpa-processor/jpa-core/src/main/resources/jpaprocessor_msg.properties
+++ b/odata2-jpa-processor/jpa-core/src/main/resources/jpaprocessor_msg.properties
@@ -34,7 +34,7 @@
 org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException.FUNC_RETURN_TYPE_EXP="OData - JPA Model Processor: Return type expected. Class: [%1$s], Method: [%2$s]"
 org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException.FUNC_RETURN_TYPE_ENTITY_NOT_FOUND="OData - JPA Model Processor: Return type not found. Class: [%1$s], Method: [%2$s], Type: [%3$s]"
 org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException.FUNC_PARAM_NAME_EXP="OData - JPA Model Processor: Parameter Name for function import expected. Class: [%1$s]", Method: [%2$s]"
-
+org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException.REF_ATTRIBUTE_NOT_FOUND="OData - JPA Model Processor: Reference attribute not found in JPA entity [%1$s]"
 #JPA Type converter Errors
 org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException.TYPE_NOT_SUPPORTED="OData - JPA Type Converter: Type [%1$s] not supported"
 
diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntityTest.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntityTest.java
index e601bd8..46d967e 100644
--- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntityTest.java
+++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAEntityTest.java
@@ -101,6 +101,7 @@
     assertEquals(ODataEntryMockUtil.VALUE_C.charAt(0), jpaTypeMock.getMC());
     assertEquals(ODataEntryMockUtil.VALUE_CARRAY, new String(jpaTypeMock.getMCArray()));
     assertEquals(ODataEntryMockUtil.VALUE_CHAR, jpaTypeMock.getMChar().toString());
+    assertEquals(ODataEntryMockUtil.VALUE_ENUM, jpaTypeMock.getMSomeEnum());
     assertEquals(ODataEntryMockUtil.VALUE_CHARARRAY, JPAEntityParser.toString(jpaTypeMock.getMCharArray()));
     assertTrue(jpaTypeMock.getMDateTime().equals(ODataEntryMockUtil.VALUE_DATE_TIME));
   }
@@ -229,6 +230,7 @@
     JPATypeMock jpaTypeMock = (JPATypeMock) jpaEntity.getJPAEntity();
     assertEquals(jpaTypeMock.getMInt(), 0);// Key should not be changed
     assertEquals(jpaTypeMock.getMString(), ODataEntryMockUtil.VALUE_MSTRING);
+    assertEquals(ODataEntryMockUtil.VALUE_ENUM, jpaTypeMock.getMSomeEnum());
     assertTrue(jpaTypeMock.getMDateTime().equals(ODataEntryMockUtil.VALUE_DATE_TIME));
   }
 
diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertorTest.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertorTest.java
index 29462c9..9325973 100644
--- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertorTest.java
+++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/model/JPATypeConvertorTest.java
@@ -42,6 +42,9 @@
   private EdmSimpleTypeKind edmSimpleKindTypeByte;
   private EdmSimpleTypeKind edmSimpleKindTypeBoolean;
   private EdmSimpleTypeKind edmSimpleKindTypeUUID;
+  private EdmSimpleTypeKind edmSimpleKindTypeStringFromEnum;
+
+  enum SomeEnum {TEST}
 
   @Test
   public void testConvertToEdmSimpleType() {
@@ -56,6 +59,7 @@
     Byte byteObj = new Byte((byte) 0);
     Boolean booleanObj = Boolean.TRUE;
     UUID uUID = new UUID(0, 0);
+    SomeEnum someEnum = SomeEnum.TEST;
 
     try {
       edmSimpleKindTypeString = JPATypeConvertor.convertToEdmSimpleType(str.getClass(), null);
@@ -68,6 +72,7 @@
       edmSimpleKindTypeBigDecimal = JPATypeConvertor.convertToEdmSimpleType(bigDecimalObj.getClass(), null);
       edmSimpleKindTypeByte = JPATypeConvertor.convertToEdmSimpleType(byteObj.getClass(), null);
       edmSimpleKindTypeBoolean = JPATypeConvertor.convertToEdmSimpleType(booleanObj.getClass(), null);
+      edmSimpleKindTypeStringFromEnum = JPATypeConvertor.convertToEdmSimpleType(someEnum.getClass(), null);
       /*
        * edmSimpleKindTypeDate = JPATypeConvertor
        * .convertToEdmSimpleType(dateObj.getClass(),null);
@@ -89,6 +94,7 @@
     assertEquals(EdmSimpleTypeKind.Boolean, edmSimpleKindTypeBoolean);
     // assertEquals(EdmSimpleTypeKind.DateTime, edmSimpleKindTypeDate);
     assertEquals(EdmSimpleTypeKind.Guid, edmSimpleKindTypeUUID);
+    assertEquals(EdmSimpleTypeKind.String, edmSimpleKindTypeStringFromEnum);
   }
 
 }
diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/EdmMockUtilV2.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/EdmMockUtilV2.java
index dc8fe8b..23208ac 100644
--- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/EdmMockUtilV2.java
+++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/EdmMockUtilV2.java
@@ -85,6 +85,8 @@
     if (entityName.equals(JPATypeMock.ENTITY_NAME)) {
       EasyMock.expect(entityType.getProperty(JPATypeMock.PROPERTY_NAME_MINT)).andReturn(
           mockEdmProperty(entityName, JPATypeMock.PROPERTY_NAME_MINT)).anyTimes();
+      EasyMock.expect(entityType.getProperty(JPATypeMock.PROPERTY_NAME_ENUM)).andReturn(
+          mockEdmProperty(entityName, JPATypeMock.PROPERTY_NAME_ENUM)).anyTimes();
       EasyMock.expect(entityType.getProperty(JPATypeMock.PROPERTY_NAME_MSTRING)).andReturn(
           mockEdmProperty(entityName, JPATypeMock.PROPERTY_NAME_MSTRING)).anyTimes();
       EasyMock.expect(entityType.getProperty(JPATypeMock.PROPERTY_NAME_MDATETIME)).andReturn(
@@ -157,6 +159,7 @@
       propertyNames.add(JPATypeMock.PROPERTY_NAME_MCARRAY);
       propertyNames.add(JPATypeMock.PROPERTY_NAME_MCHAR);
       propertyNames.add(JPATypeMock.PROPERTY_NAME_MCHARARRAY);
+      propertyNames.add(JPATypeMock.PROPERTY_NAME_ENUM);
     } else if (entityName.equals(JPARelatedTypeMock.ENTITY_NAME)) {
       propertyNames.add(JPARelatedTypeMock.PROPERTY_NAME_MLONG);
       propertyNames.add(JPARelatedTypeMock.PROPERTY_NAME_MBYTE);
@@ -246,6 +249,7 @@
     EdmProperty edmProperty = EasyMock.createMock(EdmProperty.class);
 
     if (propertyName.equals(JPATypeMock.PROPERTY_NAME_MINT) ||
+        propertyName.equals(JPATypeMock.PROPERTY_NAME_ENUM) ||
         propertyName.equals(JPATypeMock.PROPERTY_NAME_MSTRING) ||
         propertyName.equals(JPATypeMock.PROPERTY_NAME_MDATETIME) ||
         propertyName.equals(JPATypeMock.PROPERTY_NAME_MBLOB) ||
@@ -266,6 +270,7 @@
       EasyMock.expect(edmProperty.getType()).andReturn(edmType).anyTimes();
       EasyMock.expect(edmType.getKind()).andReturn(EdmTypeKind.SIMPLE).anyTimes();
       if (propertyName.equals(JPATypeMock.PROPERTY_NAME_MSTRING) ||
+          propertyName.equals(JPATypeMock.PROPERTY_NAME_ENUM) ||
           propertyName.equals(JPATypeMock.PROPERTY_NAME_MCARRAY) ||
           propertyName.equals(JPATypeMock.PROPERTY_NAME_MC) ||
           propertyName.equals(JPATypeMock.PROPERTY_NAME_MCHAR) ||
@@ -351,6 +356,9 @@
     } else if (propertyName.equals(JPATypeMock.PROPERTY_NAME_MSTRING)) {
       mapping.setJPAType(String.class);
       ((Mapping) mapping).setInternalName(JPATypeMock.PROPERTY_NAME_MSTRING);
+    } else if (propertyName.equals(JPATypeMock.PROPERTY_NAME_ENUM)) {
+      mapping.setJPAType(JPATypeMock.JPATypeMockEnum.class);
+      ((Mapping) mapping).setInternalName(JPATypeMock.PROPERTY_NAME_ENUM);
     } else if (propertyName.equals(JPATypeMock.PROPERTY_NAME_MBLOB)) {
       mapping.setJPAType(Blob.class);
       ((Mapping) mapping).setInternalName(JPATypeMock.PROPERTY_NAME_MBLOB);
diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/JPATypeMock.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/JPATypeMock.java
index 59701fb..3d7ddcc 100644
--- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/JPATypeMock.java
+++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/JPATypeMock.java
@@ -28,6 +28,8 @@
 /* ========================================================================= */
 public class JPATypeMock {
 
+  enum JPATypeMockEnum {VALUE, MORE_VALUE}
+
   public static final String ENTITY_NAME = "JPATypeMock";
   public static final String PROPERTY_NAME_MINT = "mInt";
   public static final String PROPERTY_NAME_MSTRING = "mString";
@@ -40,6 +42,7 @@
   public static final String PROPERTY_NAME_MCARRAY = "mCArray";
   public static final String PROPERTY_NAME_MKEY = "key";
   public static final String PROPERTY_NAME_MCOMPLEXTYPE = "complexType";
+  public static final String PROPERTY_NAME_ENUM = "mSomeEnum";
 
   public static final String NAVIGATION_PROPERTY_X = "mRelatedEntity";
   public static final String NAVIGATION_PROPERTY_XS = "mRelatedEntities";
@@ -55,6 +58,7 @@
   private char[] mCArray;
   private Character mChar;
   private Character[] mCharArray;
+  private JPATypeMockEnum mSomeEnum;
 
   public Clob getMClob() {
     return mClob;
@@ -163,6 +167,14 @@
     this.mBlob = mBlob;
   }
 
+  public JPATypeMockEnum getMSomeEnum() {
+    return mSomeEnum;
+  }
+
+  public void setMSomeEnum(JPATypeMockEnum mSomeEnum) {
+    this.mSomeEnum = mSomeEnum;
+  }
+
   /* ========================================================================= */
   public static class JPATypeEmbeddableMock {
 
diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/ODataEntryMockUtil.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/ODataEntryMockUtil.java
index 4f95a0e..e5a081c 100644
--- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/ODataEntryMockUtil.java
+++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/data/ODataEntryMockUtil.java
@@ -52,6 +52,7 @@
   public static final float VALUE_MFLOAT = 2.00F;
   public static final UUID VALUE_UUID = UUID.fromString("38400000-8cf0-11bd-b23e-10b96e4ef00d");
   public static final short VALUE_SHORT = 2;
+  public static final JPATypeMock.JPATypeMockEnum VALUE_ENUM = JPATypeMock.JPATypeMockEnum.VALUE;
 
   public static ODataEntry mockODataEntry(final String entityName) {
     ODataEntry oDataEntry = EasyMock.createMock(ODataEntry.class);
@@ -77,6 +78,7 @@
 
     if (entityName.equals(JPATypeMock.ENTITY_NAME)) {
       propertyMap.put(JPATypeMock.PROPERTY_NAME_MINT, VALUE_MINT);
+      propertyMap.put(JPATypeMock.PROPERTY_NAME_ENUM, "VALUE");
 
       VALUE_DATE_TIME = Calendar.getInstance(TimeZone.getDefault());
       VALUE_DATE_TIME.set(2013, 1, 1, 1, 1, 1);
diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmPropertyTest.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmPropertyTest.java
index c204d1f..f76a50f 100644
--- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmPropertyTest.java
+++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/model/JPAEdmPropertyTest.java
@@ -24,9 +24,13 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.persistence.Column;
+import javax.persistence.JoinColumn;
 import javax.persistence.metamodel.Attribute;
 import javax.persistence.metamodel.Attribute.PersistentAttributeType;
 import javax.persistence.metamodel.EmbeddableType;
@@ -34,9 +38,11 @@
 import javax.persistence.metamodel.Metamodel;
 import javax.persistence.metamodel.Type;
 
+import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
 import org.apache.olingo.odata2.api.edm.FullQualifiedName;
 import org.apache.olingo.odata2.api.edm.provider.Association;
 import org.apache.olingo.odata2.api.edm.provider.AssociationEnd;
+import org.apache.olingo.odata2.api.edm.provider.NavigationProperty;
 import org.apache.olingo.odata2.api.edm.provider.Schema;
 import org.apache.olingo.odata2.jpa.processor.api.access.JPAEdmBuilder;
 import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException;
@@ -51,15 +57,18 @@
 import org.apache.olingo.odata2.jpa.processor.core.mock.model.JPAEdmMockData.SimpleType;
 import org.apache.olingo.odata2.jpa.processor.core.mock.model.JPAEmbeddableTypeMock;
 import org.apache.olingo.odata2.jpa.processor.core.mock.model.JPAEntityTypeMock;
+import org.apache.olingo.odata2.jpa.processor.core.mock.model.JPAJavaMemberMock;
 import org.apache.olingo.odata2.jpa.processor.core.mock.model.JPAMetaModelMock;
 import org.apache.olingo.odata2.jpa.processor.core.mock.model.JPAPluralAttributeMock;
 import org.apache.olingo.odata2.jpa.processor.core.mock.model.JPASingularAttributeMock;
+import org.easymock.EasyMock;
 import org.junit.Test;
 
 public class JPAEdmPropertyTest extends JPAEdmTestModelView {
 
   private JPAEdmPropertyTest objJPAEdmPropertyTest;
   private JPAEdmProperty objJPAEdmProperty;
+  private static java.lang.String testCase = "Default";
 
   private static PersistentAttributeType ATTRIBUTE_TYPE = PersistentAttributeType.BASIC;
 
@@ -157,8 +166,10 @@
   @Test
   public void testBuildManyToOne() {
     ATTRIBUTE_TYPE = PersistentAttributeType.MANY_TO_ONE;
+    testCase = "Default";
     objJPAEdmPropertyTest = new JPAEdmPropertyTest();
     objJPAEdmProperty = new JPAEdmProperty(objJPAEdmPropertyTest);
+
     try {
       objJPAEdmProperty.getBuilder().build();
     } catch (ODataJPAModelException e) {
@@ -167,7 +178,32 @@
       fail(ODataJPATestConstants.EXCEPTION_MSG_PART_1 + e.getMessage() + ODataJPATestConstants.EXCEPTION_MSG_PART_2);
     }
 
-    assertNotNull(objJPAEdmProperty.getJPAEdmNavigationPropertyView().getEdmNavigationProperty());
+    NavigationProperty navigationProperty =
+        objJPAEdmProperty.getJPAEdmNavigationPropertyView().getEdmNavigationProperty();
+    assertNotNull(navigationProperty);
+  }
+
+  @Test
+  public void testBuildManyToOneNoJoinColumnNames() {
+    ATTRIBUTE_TYPE = PersistentAttributeType.MANY_TO_ONE;
+    testCase = "NoJoinColumnNames";
+    objJPAEdmPropertyTest = new JPAEdmPropertyTest();
+    objJPAEdmProperty = new JPAEdmProperty(objJPAEdmPropertyTest);
+
+    try {
+      objJPAEdmProperty.getBuilder().build();
+    } catch (ODataJPAModelException e) {
+      fail(ODataJPATestConstants.EXCEPTION_MSG_PART_1 + e.getMessage() + ODataJPATestConstants.EXCEPTION_MSG_PART_2);
+    } catch (ODataJPARuntimeException e) {
+      fail(ODataJPATestConstants.EXCEPTION_MSG_PART_1 + e.getMessage() + ODataJPATestConstants.EXCEPTION_MSG_PART_2);
+    }
+
+    NavigationProperty navigationProperty =
+        objJPAEdmProperty.getJPAEdmNavigationPropertyView().getEdmNavigationProperty();
+    assertNotNull(navigationProperty);
+    assertEquals("String", navigationProperty.getFromRole());
+    assertEquals("salesorderprocessing", navigationProperty.getToRole());
+    assertEquals("StringDetails", navigationProperty.getName());
   }
 
   @Override
@@ -227,9 +263,20 @@
   @Override
   public Association getEdmAssociation() {
     Association association = new Association();
+    AssociationEnd end1 = new AssociationEnd();
+    end1.setType(new FullQualifiedName("salesorderprocessing", "SalesOrderHeader"));
+    end1.setMultiplicity(EdmMultiplicity.MANY);
+    end1.setRole("salesorderprocessing");
+
+    AssociationEnd end2 = new AssociationEnd();
+    end2.setType(new FullQualifiedName("String", "SalesOrderHeader"));
+    end2.setMultiplicity(EdmMultiplicity.ONE);
+    end2.setRole("String");
+
     association
-        .setEnd1(new AssociationEnd().setType(new FullQualifiedName("salesorderprocessing", "SalesOrderHeader")));
-    association.setEnd2(new AssociationEnd().setType(new FullQualifiedName("salesorderprocessing", "String")));
+        .setEnd1(end1);
+    association.setEnd2(end2);
+    association.setName("salesorderprocessing_String");
 
     return association;
   }
@@ -254,6 +301,58 @@
     return this;
   }
 
+  @Override
+  public Attribute<?, ?> getJPAReferencedAttribute() {
+    JPAEdmAttribute<Object, String> refAttribute =
+        new JPAEdmAttribute<Object, String>(java.lang.String.class, "SOLITID");
+
+    return refAttribute;
+  }
+
+  @SuppressWarnings("hiding")
+  private static class JPAEdmAttribute<Object, String> extends JPASingularAttributeMock<Object, String> {
+
+    @Override
+    public PersistentAttributeType getPersistentAttributeType() {
+      if (attributeName.equals("SOLITID")) {
+        return PersistentAttributeType.BASIC;
+      }
+      return ATTRIBUTE_TYPE;
+    }
+
+    Class<String> clazz;
+    java.lang.String attributeName;
+
+    public JPAEdmAttribute(final Class<String> javaType, final java.lang.String name) {
+      this.clazz = javaType;
+      this.attributeName = name;
+
+    }
+
+    @Override
+    public Class<String> getJavaType() {
+      return clazz;
+    }
+
+    @Override
+    public java.lang.String getName() {
+      return this.attributeName;
+    }
+
+    @Override
+    public boolean isId() {
+      return true;
+    }
+
+    @Override
+    public Member getJavaMember() {
+      if (this.attributeName.equals("SOLITID")) {
+        return new JPAJavaMember();
+      }
+      return null;
+    }
+  }
+
   private class JPAEdmMetaModel extends JPAMetaModelMock {
     Set<EntityType<?>> entities;
     Set<EmbeddableType<?>> embeddableSet;
@@ -265,7 +364,7 @@
 
     @Override
     public Set<EntityType<?>> getEntities() {
-      entities.add(new JPAEdmEntityType());
+      entities.add(new JPAEdmEntityType<String>());
       return entities;
     }
 
@@ -275,16 +374,39 @@
       return embeddableSet;
     }
 
-    private class JPAEdmEntityType extends JPAEntityTypeMock<String> {
-      @Override
-      public String getName() {
-        return "SalesOrderHeader";
-      }
+    @SuppressWarnings("unchecked")
+    @Override
+    public <X> EntityType<X> entity(final Class<X> arg0) {
+      JPAEdmRefEntityType<String> refEntityType = new JPAEdmRefEntityType<String>();
+      return (EntityType<X>) refEntityType;
+
     }
   }
 
   @SuppressWarnings("hiding")
-  private class JPAEdmEntityType<String> extends JPAEntityTypeMock<String> {
+  private static class JPAEdmRefEntityType<String> extends JPAEntityTypeMock<String> {
+    Set<Attribute<? super String, ?>> attributeSet = new HashSet<Attribute<? super String, ?>>();
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private void setValuesToSet() {
+      attributeSet.add((Attribute<? super String, String>) new JPAEdmAttribute(java.lang.String.class, "SOLITID"));
+      attributeSet.add((Attribute<? super String, String>) new JPAEdmAttribute(java.lang.String.class, "SONAME"));
+    }
+
+    @Override
+    public Set<Attribute<? super String, ?>> getAttributes() {
+      setValuesToSet();
+      return attributeSet;
+    }
+
+    @Override
+    public java.lang.String getName() {
+      return "salesorderitemdetails";
+    }
+  }
+
+  @SuppressWarnings("hiding")
+  private static class JPAEdmEntityType<String> extends JPAEntityTypeMock<String> {
     Set<Attribute<? super String, ?>> attributeSet = new HashSet<Attribute<? super String, ?>>();
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -306,6 +428,16 @@
     }
 
     private class JPAEdmPluralAttribute extends JPAPluralAttributeMock {
+
+      @Override
+      public Member getJavaMember() {
+        if (ATTRIBUTE_TYPE.equals(PersistentAttributeType.MANY_TO_ONE)) {
+          return new JPAJavaMember();
+        }
+        return null;
+
+      }
+
       @Override
       public java.lang.String getName() {
         return "salesorderheaderdetails";
@@ -338,38 +470,6 @@
         };
       }
     }
-
-    private class JPAEdmAttribute<Object, String> extends JPASingularAttributeMock<Object, String> {
-
-      @Override
-      public PersistentAttributeType getPersistentAttributeType() {
-        return ATTRIBUTE_TYPE;
-      }
-
-      Class<String> clazz;
-      java.lang.String attributeName;
-
-      public JPAEdmAttribute(final Class<String> javaType, final java.lang.String name) {
-        this.clazz = javaType;
-        this.attributeName = name;
-
-      }
-
-      @Override
-      public Class<String> getJavaType() {
-        return clazz;
-      }
-
-      @Override
-      public java.lang.String getName() {
-        return this.attributeName;
-      }
-
-      @Override
-      public boolean isId() {
-        return true;
-      }
-    }
   }
 
   @SuppressWarnings("hiding")
@@ -435,4 +535,46 @@
 
   }
 
+  private static class JPAJavaMember extends JPAJavaMemberMock {
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends Annotation> T getAnnotation(final Class<T> annotationClass) {
+
+      if (annotationClass.equals(JoinColumn.class)) {
+
+        if (testCase.equals("Default")) {
+          JoinColumn joinColumn = EasyMock.createMock(JoinColumn.class);
+          EasyMock.expect(joinColumn.name()).andReturn("FSOID").anyTimes();
+          EasyMock.expect(joinColumn.referencedColumnName()).andReturn("SOLITID").anyTimes();
+          EasyMock.expect(joinColumn.insertable()).andReturn(true).anyTimes();
+          EasyMock.expect(joinColumn.updatable()).andReturn(true).anyTimes();
+          EasyMock.replay(joinColumn);
+          return (T) joinColumn;
+        } else if (testCase.equals("NoJoinColumnNames")) {
+          JoinColumn joinColumn = EasyMock.createMock(JoinColumn.class);
+          EasyMock.expect(joinColumn.name()).andReturn("").anyTimes();
+          EasyMock.expect(joinColumn.referencedColumnName()).andReturn("").anyTimes();
+          EasyMock.expect(joinColumn.insertable()).andReturn(true).anyTimes();
+          EasyMock.expect(joinColumn.updatable()).andReturn(true).anyTimes();
+          EasyMock.replay(joinColumn);
+          return (T) joinColumn;
+        }
+
+      } else {
+
+        if (testCase.equals("Default")) {
+          Column column = EasyMock.createMock(Column.class);
+          EasyMock.expect(column.name()).andReturn("SOLITID").anyTimes();
+          EasyMock.expect(column.nullable()).andReturn(true).anyTimes();
+          EasyMock.expect(column.length()).andReturn(30).anyTimes();
+          EasyMock.replay(column);
+          return (T) column;
+        }
+
+      }
+      return null;
+    }
+  }
+
 }
diff --git a/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/model/Customer.java b/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/model/Customer.java
index 12e38ac..3333b1f 100644
--- a/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/model/Customer.java
+++ b/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/model/Customer.java
@@ -23,6 +23,8 @@
 import javax.persistence.Column;
 import javax.persistence.Embedded;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
@@ -40,6 +42,10 @@
   @Column(name = "NAME")
   private String name;
 
+  @Enumerated(EnumType.STRING)
+  @Column(name = "IMPORTANCE")
+  private Importance importance;
+
   @Embedded
   private Address address;
 
@@ -77,13 +83,13 @@
     this.name = name;
   }
 
-//  public List<SalesOrderHeader> getOrders() {
-//    return orders;
-//  }
-//
-//  public void setOrders(final List<SalesOrderHeader> orders) {
-//    this.orders = orders;
-//  }
+  public Importance getImportance() {
+    return importance;
+  }
+
+  public void setImportance(Importance importance) {
+    this.importance = importance;
+  }
 
   public Address getAddress() {
     return address;
diff --git a/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/model/Importance.java b/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/model/Importance.java
new file mode 100644
index 0000000..6cd8057
--- /dev/null
+++ b/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/model/Importance.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * 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.odata2.jpa.processor.ref.model;
+
+public enum Importance {
+  LOW, MEDIUM, HIGH, VIP
+}
diff --git a/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/util/CustomerImageLoader.java b/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/util/CustomerImageLoader.java
new file mode 100644
index 0000000..e0a4841
--- /dev/null
+++ b/odata2-jpa-processor/jpa-ref/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/util/CustomerImageLoader.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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.odata2.jpa.processor.ref.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class CustomerImageLoader {
+  public static byte[] loadImage(Long customerId) {
+    String name = null;
+    if (customerId == 1L) {
+      name = "/Customer_1.png";
+    } else if (customerId == 2L) {
+      name = "/Customer_2.png";
+    } else {
+      return null;
+    }
+
+    InputStream is = CustomerImageLoader.class.getResourceAsStream(name);
+    ByteArrayOutputStream os = new ByteArrayOutputStream();
+    int b = 0;
+    try {
+      while ((b = is.read()) != -1) {
+        os.write(b);
+      }
+    } catch (IOException e) {
+      return null;
+    }
+    return os.toByteArray();
+  }
+
+}
diff --git a/odata2-jpa-processor/jpa-ref/src/main/resources/Customer_1.png b/odata2-jpa-processor/jpa-ref/src/main/resources/Customer_1.png
new file mode 100644
index 0000000..f817682
--- /dev/null
+++ b/odata2-jpa-processor/jpa-ref/src/main/resources/Customer_1.png
Binary files differ
diff --git a/odata2-jpa-processor/jpa-ref/src/main/resources/Customer_2.png b/odata2-jpa-processor/jpa-ref/src/main/resources/Customer_2.png
new file mode 100644
index 0000000..144ed93
--- /dev/null
+++ b/odata2-jpa-processor/jpa-ref/src/main/resources/Customer_2.png
Binary files differ
diff --git a/odata2-jpa-processor/jpa-web/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/extension/CustomerImageProcessor.java b/odata2-jpa-processor/jpa-web/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/extension/CustomerImageProcessor.java
new file mode 100644
index 0000000..f930c80
--- /dev/null
+++ b/odata2-jpa-processor/jpa-web/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/extension/CustomerImageProcessor.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.odata2.jpa.processor.ref.extension;
+
+import org.apache.olingo.odata2.api.annotation.edm.EdmFacets;
+import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImport;
+import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImport.ReturnType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImport.ReturnType.Type;
+import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImportParameter;
+import org.apache.olingo.odata2.jpa.processor.ref.util.CustomerImageLoader;
+
+public class CustomerImageProcessor {
+
+  @EdmFunctionImport(returnType = @ReturnType(type = Type.SIMPLE))
+  public byte[] getImage(
+      @EdmFunctionImportParameter(name = "CustomerId", facets = @EdmFacets(nullable = false)) Long customerId) {
+    return CustomerImageLoader.loadImage(customerId);
+  }
+}
diff --git a/odata2-jpa-processor/jpa-web/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/extension/SalesOrderProcessingExtension.java b/odata2-jpa-processor/jpa-web/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/extension/SalesOrderProcessingExtension.java
index c3065ec..1553ba6 100644
--- a/odata2-jpa-processor/jpa-web/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/extension/SalesOrderProcessingExtension.java
+++ b/odata2-jpa-processor/jpa-web/src/main/java/org/apache/olingo/odata2/jpa/processor/ref/extension/SalesOrderProcessingExtension.java
@@ -63,6 +63,7 @@
   @Override
   public void extendWithOperation(final JPAEdmSchemaView view) {
     view.registerOperations(SalesOrderHeaderProcessor.class, null);
+    view.registerOperations(CustomerImageProcessor.class, null);
 
   }
 
diff --git a/odata2-jpa-processor/jpa-web/src/main/resources/SQL_Insert_Customer.properties b/odata2-jpa-processor/jpa-web/src/main/resources/SQL_Insert_Customer.properties
index a42f23e..16e13bd 100644
--- a/odata2-jpa-processor/jpa-web/src/main/resources/SQL_Insert_Customer.properties
+++ b/odata2-jpa-processor/jpa-web/src/main/resources/SQL_Insert_Customer.properties
@@ -16,8 +16,8 @@
 #        specific language governing permissions and limitations
 #        under the License.
 #-------------------------------------------------------------------------------
-query_1 = INSERT INTO T_CUSTOMER (ID , NAME, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(100,'Bob Bryan',7,'5 cross Street', 'London', 'UK',  'E7','2012-11-01 00:01:00');
-query_2 = INSERT INTO T_CUSTOMER (ID , NAME, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(200,'Mike Bryan',7,'8 cross Street', 'New York', 'USA',  '10011','2012-11-01 00:01:00');
-query_3 = INSERT INTO T_CUSTOMER (ID , NAME, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(201,'Steve Roger',7,'9 cross Street', 'Mumbai', 'India', '200101','2012-11-01 00:01:00');
-query_4 = INSERT INTO T_CUSTOMER (ID , NAME, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(101,'Pac Man',7,'25 cross Street', 'Frankfurt', 'Germany',  '60001','2012-11-01 00:01:00');
-query_5 = INSERT INTO T_CUSTOMER (ID , NAME, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(202,'Bolt Man',7,'25 cross Street', 'Toronto', 'Canada',  'NE','2012-11-01 00:01:00');
\ No newline at end of file
+query_1 = INSERT INTO T_CUSTOMER (ID , NAME, IMPORTANCE, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(100,'Bob Bryan','VIP',7,'5 cross Street', 'London', 'UK',  'E7','2012-11-01 00:01:00');
+query_2 = INSERT INTO T_CUSTOMER (ID , NAME, IMPORTANCE, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(200,'Mike Bryan','LOW',7,'8 cross Street', 'New York', 'USA',  '10011','2012-11-01 00:01:00');
+query_3 = INSERT INTO T_CUSTOMER (ID , NAME, IMPORTANCE, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(201,'Steve Roger','LOW',7,'9 cross Street', 'Mumbai', 'India', '200101','2012-11-01 00:01:00');
+query_4 = INSERT INTO T_CUSTOMER (ID , NAME, IMPORTANCE, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(101,'Pac Man','LOW',7,'25 cross Street', 'Frankfurt', 'Germany',  '60001','2012-11-01 00:01:00');
+query_5 = INSERT INTO T_CUSTOMER (ID , NAME, IMPORTANCE, HOUSE_NUMBER , STREET_NAME, CITY, COUNTRY, PINCODE,CREATED_AT) VALUES(202,'Bolt Man','LOW',7,'25 cross Street', 'Toronto', 'Canada',  'NE','2012-11-01 00:01:00');
\ No newline at end of file
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java
index 9ec21cd..85242c6 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java
@@ -59,7 +59,6 @@
             request.getContentId());
       }
       
-      writer.append(CRLF);  // CRLF belongs to the boundary delimiter or boundary closing delimiter
     }
     writer.append("--").append(boundary).append("--");
     InputStream batchRequestBody;
@@ -109,6 +108,8 @@
 
     if (body != null && !body.isEmpty()) {
       writer.append(body);
+    } else {
+      writer.append(CRLF);
     }
   }
 
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java
index be189d3..3840cdf 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java
@@ -33,7 +33,7 @@
 public class BatchResponseWriter {
   private static final String COLON = ":";
   private static final String SP = " ";
-  private static final String LF = "\r\n";
+  private static final String CRLF = "\r\n";
   private ResponseWriter writer = new ResponseWriter();
 
   public ODataResponse writeResponse(final List<BatchResponsePart> batchResponseParts) throws BatchException {
@@ -49,18 +49,18 @@
   private void appendChangeSet(final BatchResponsePart batchResponsePart) throws BatchException {
     String boundary = BatchHelper.generateBoundary("changeset");
     writer.append(HttpHeaders.CONTENT_TYPE).append(COLON).append(SP)
-        .append("multipart/mixed; boundary=" + boundary).append(LF).append(LF);
+        .append("multipart/mixed; boundary=" + boundary).append(CRLF).append(CRLF);
     for (ODataResponse response : batchResponsePart.getResponses()) {
-      writer.append("--").append(boundary).append(LF);
+      writer.append("--").append(boundary).append(CRLF);
       appendResponsePartBody(response);
     }
-    writer.append("--").append(boundary).append("--").append(LF).append(LF);
+    writer.append("--").append(boundary).append("--").append(CRLF);
   }
 
   private void appendResponsePart(final List<BatchResponsePart> batchResponseParts, final String boundary)
       throws BatchException {
     for (BatchResponsePart batchResponsePart : batchResponseParts) {
-      writer.append("--").append(boundary).append(LF);
+      writer.append("--").append(boundary).append(CRLF);
       if (batchResponsePart.isChangeSet()) {
         appendChangeSet(batchResponsePart);
       } else {
@@ -73,16 +73,16 @@
 
   private void appendResponsePartBody(final ODataResponse response) throws BatchException {
     writer.append(HttpHeaders.CONTENT_TYPE).append(COLON).append(SP)
-        .append(HttpContentType.APPLICATION_HTTP).append(LF);
+        .append(HttpContentType.APPLICATION_HTTP).append(CRLF);
     writer.append(BatchHelper.HTTP_CONTENT_TRANSFER_ENCODING).append(COLON).append(SP)
-        .append(BatchHelper.BINARY_ENCODING).append(LF);
+        .append(BatchHelper.BINARY_ENCODING).append(CRLF);
     if (response.getHeader(BatchHelper.MIME_HEADER_CONTENT_ID) != null) {
       writer.append(BatchHelper.HTTP_CONTENT_ID).append(COLON).append(SP)
-          .append(response.getHeader(BatchHelper.MIME_HEADER_CONTENT_ID)).append(LF);
+          .append(response.getHeader(BatchHelper.MIME_HEADER_CONTENT_ID)).append(CRLF);
     }
-    writer.append(LF);
+    writer.append(CRLF);
     writer.append("HTTP/1.1").append(SP).append(String.valueOf(response.getStatus().getStatusCode())).append(SP)
-        .append(response.getStatus().getInfo()).append(LF);
+        .append(response.getStatus().getInfo()).append(CRLF);
     appendHeader(response);
     if (!HttpStatusCodes.NO_CONTENT.equals(response.getStatus())) {
       String body;
@@ -93,20 +93,23 @@
         body = response.getEntity().toString();
       }
       writer.append(HttpHeaders.CONTENT_LENGTH).append(COLON).append(SP)
-          .append(String.valueOf(BatchHelper.getBytes(body).length)).append(LF).append(LF);
+          .append(String.valueOf(BatchHelper.getBytes(body).length)).append(CRLF).append(CRLF);
       writer.append(body);
+    } else {
+      // No header if status code equals to 204 (No content)
+      writer.append(CRLF);
     }
-    writer.append(LF).append(LF);
+    writer.append(CRLF);
   }
 
   private void appendHeader(final ODataResponse response) {
     for (String name : response.getHeaderNames()) {
       if (!BatchHelper.MIME_HEADER_CONTENT_ID.equalsIgnoreCase(name)
           && !BatchHelper.REQUEST_HEADER_CONTENT_ID.equalsIgnoreCase(name)) {
-        writer.append(name).append(COLON).append(SP).append(response.getHeader(name)).append(LF);
+        writer.append(name).append(COLON).append(SP).append(response.getHeader(name)).append(CRLF);
       } else if (BatchHelper.REQUEST_HEADER_CONTENT_ID.equalsIgnoreCase(name)) {
         writer.append(BatchHelper.HTTP_CONTENT_ID).append(COLON).append(SP)
-            .append(response.getHeader(name)).append(LF);
+            .append(response.getHeader(name)).append(CRLF);
       }
     }
   }
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumer.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumer.java
index 0cc25e8..b2e62b5 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumer.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumer.java
@@ -104,7 +104,7 @@
               .getLocalName())) {
             dataServices.setDataServiceVersion(reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, "DataServiceVersion"));
           }
-        } 
+        }
       }
 
       if (!reader.isEndElement() || !XmlMetadataConstants.EDMX_TAG.equals(reader.getLocalName())) {
@@ -870,17 +870,32 @@
     if (!annotationAttributes.isEmpty()) {
       aElement.setAttributes(annotationAttributes);
     }
-    while (reader.hasNext() && !(reader.isEndElement() && aElement.getName() != null
-        && aElement.getName().equals(reader.getLocalName()))) {
+
+    boolean justRead = false;
+    if (reader.hasNext()) {
       reader.next();
+      justRead = true;
+    }
+
+    while (justRead && !(reader.isEndElement() && aElement.getName() != null
+        && aElement.getName().equals(reader.getLocalName()))) {
+      justRead = false;
       if (reader.isStartElement()) {
         annotationElements.add(readAnnotationElement(reader));
+        if (reader.hasNext()) {
+          reader.next();
+          justRead = true;
+        }
       } else if (reader.isCharacters()) {
         String elementText = "";
         do {
+          justRead = false;
           elementText = elementText + reader.getText();
-          reader.next();
-        } while (reader.isCharacters());
+          if (reader.hasNext()) {
+            reader.next();
+            justRead = true;
+          }
+        } while (justRead && reader.isCharacters());
         aElement.setText(elementText);
       }
     }
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataRootLocator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataRootLocator.java
index d9a91cc..57a8bc8 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataRootLocator.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataRootLocator.java
@@ -6,9 +6,9 @@
  * 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
@@ -49,7 +49,7 @@
  * The first segment defined by a servlet mapping belong to customer context and the following segments are OData
  * specific.
  * </p>
- * 
+ *
  */
 @Path("/")
 public class ODataRootLocator {
@@ -102,13 +102,9 @@
       return handleRedirect();
     }
 
-    ODataServiceFactory serviceFactory = createServiceFactoryFromContext(app, servletRequest, servletConfig);
+    ODataServiceFactory serviceFactory = getServiceFactory();
 
-    int pathSplit = 0;
-    final String pathSplitAsString = servletConfig.getInitParameter(ODataServiceFactory.PATH_SPLIT_LABEL);
-    if (pathSplitAsString != null) {
-      pathSplit = Integer.parseInt(pathSplitAsString);
-    }
+    int pathSplit = getPathSplit();
 
     final SubLocatorParameter param = new SubLocatorParameter();
     param.setServiceFactory(serviceFactory);
@@ -122,6 +118,19 @@
     return ODataSubLocator.create(param);
   }
 
+  public ODataServiceFactory getServiceFactory() {
+    return createServiceFactoryFromContext(app, servletRequest, servletConfig);
+  }
+
+  public int getPathSplit() {
+    int pathSplit = 0;
+    final String pathSplitAsString = servletConfig.getInitParameter(ODataServiceFactory.PATH_SPLIT_LABEL);
+    if (pathSplitAsString != null) {
+      pathSplit = Integer.parseInt(pathSplitAsString);
+    }
+    return pathSplit;
+  }
+
   public static ODataServiceFactory createServiceFactoryFromContext(final Application app,
       final HttpServletRequest servletRequest,
       final ServletConfig servletConfig) {
@@ -142,8 +151,7 @@
           factoryClass = Class.forName(factoryClassName, true, cl);
         }
       }
-      ODataServiceFactory serviceFactory = (ODataServiceFactory) factoryClass.newInstance();
-      return serviceFactory;
+      return (ODataServiceFactory) factoryClass.newInstance();
     } catch (Exception e) {
       throw new ODataRuntimeException("Exception during ODataServiceFactory creation occured.", e);
     }
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/RestUtil.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/RestUtil.java
index 8cb200d..b726f9b 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/RestUtil.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/RestUtil.java
@@ -37,6 +37,7 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
 import org.apache.olingo.odata2.api.exception.ODataBadRequestException;
@@ -180,7 +181,8 @@
   public static PathInfoImpl buildODataPathInfo(final SubLocatorParameter param) throws ODataException {
     PathInfoImpl pathInfo = splitPath(param);
 
-    pathInfo.setServiceRoot(buildBaseUri(param.getServletRequest(), pathInfo.getPrecedingSegments()));
+    pathInfo.setServiceRoot(buildBaseUri(param.getUriInfo(),
+        param.getServletRequest(), pathInfo.getPrecedingSegments()));
     pathInfo.setRequestUri(buildRequestUri(param.getServletRequest()));
 
     return pathInfo;
@@ -224,10 +226,10 @@
     return pathInfo;
   }
 
-  private static URI buildBaseUri(final HttpServletRequest request,
+  private static URI buildBaseUri(final UriInfo uriInfo, final HttpServletRequest request,
       final List<PathSegment> precedingPathSegments) throws ODataException {
     try {
-      String path = request.getContextPath() + request.getServletPath();
+      String path = uriInfo.getBaseUri().getPath();
       UriBuilder uriBuilder = UriBuilder.fromUri(path);
       for (final PathSegment ps : precedingPathSegments) {
         uriBuilder = uriBuilder.path(ps.getPath());
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataExceptionWrapper.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataExceptionWrapper.java
index 7edd411..e2069d4 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataExceptionWrapper.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataExceptionWrapper.java
@@ -28,6 +28,7 @@
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.olingo.odata2.api.ODataServiceFactory;
 import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
 import org.apache.olingo.odata2.api.ep.EntityProvider;
@@ -37,9 +38,11 @@
 import org.apache.olingo.odata2.api.exception.ODataException;
 import org.apache.olingo.odata2.api.exception.ODataHttpException;
 import org.apache.olingo.odata2.api.exception.ODataMessageException;
+import org.apache.olingo.odata2.api.processor.ODataErrorCallback;
 import org.apache.olingo.odata2.api.processor.ODataErrorContext;
 import org.apache.olingo.odata2.api.processor.ODataResponse;
 import org.apache.olingo.odata2.core.commons.ContentType;
+import org.apache.olingo.odata2.core.ep.ProviderFacadeImpl;
 import org.apache.olingo.odata2.core.exception.MessageService;
 import org.apache.olingo.odata2.core.exception.MessageService.Message;
 import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
@@ -57,8 +60,9 @@
   private final Locale messageLocale;
   private final URI requestUri;
   private final Map<String, List<String>> httpRequestHeaders;
+  private final ODataErrorCallback callback;
 
-  public ODataExceptionWrapper(final HttpServletRequest req) {
+  public ODataExceptionWrapper(final HttpServletRequest req, ODataServiceFactory serviceFactory) {
     try {
       requestUri = new URI(req.getRequestURI());
     } catch (URISyntaxException e) {
@@ -70,6 +74,7 @@
     List<String> acceptHeaders = RestUtil.extractAcceptHeaders(req.getHeader("Accept"));
     contentType = getContentType(queryParameters, acceptHeaders).toContentTypeString();
     messageLocale = MessageService.getSupportedLocale(getLanguages(acceptableLanguages), DEFAULT_RESPONSE_LOCALE);
+    callback = serviceFactory.getCallback(ODataErrorCallback.class);
   }
 
   public ODataResponse wrapInExceptionResponse(final Exception exception) {
@@ -83,11 +88,11 @@
       }
 
       ODataResponse oDataResponse;
-      // if (callback != null) {
-      // oDataResponse = handleErrorCallback(callback);
-      // } else {
-      oDataResponse = EntityProvider.writeErrorDocument(errorContext);
-      // }
+      if (callback != null) {
+        oDataResponse = handleErrorCallback(callback);
+      } else {
+        oDataResponse = EntityProvider.writeErrorDocument(errorContext);
+      }
       if (!oDataResponse.containsHeader(org.apache.olingo.odata2.api.commons.HttpHeaders.CONTENT_TYPE)) {
         oDataResponse = ODataResponse.fromResponse(oDataResponse).contentHeader(contentType).build();
       }
@@ -100,6 +105,18 @@
     }
   }
 
+  private ODataResponse handleErrorCallback(final ODataErrorCallback callback) throws EntityProviderException {
+    ODataResponse oDataResponse;
+    try {
+      oDataResponse = callback.handleError(errorContext);
+    } catch (ODataApplicationException e) {
+      fillErrorContext(e);
+      enhanceContextWithApplicationException(e);
+      oDataResponse = new ProviderFacadeImpl().writeErrorDocument(errorContext);
+    }
+    return oDataResponse;
+  }
+
   private Exception extractException(final Exception exception) {
     if (exception instanceof ODataException) {
       ODataException odataException = (ODataException) exception;
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
index 465d52e..5607025 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
@@ -33,7 +33,6 @@
 import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
 import org.apache.olingo.odata2.api.exception.MessageReference;
 import org.apache.olingo.odata2.api.exception.ODataBadRequestException;
-import org.apache.olingo.odata2.api.exception.ODataException;
 import org.apache.olingo.odata2.api.exception.ODataHttpException;
 import org.apache.olingo.odata2.api.exception.ODataInternalServerErrorException;
 import org.apache.olingo.odata2.api.exception.ODataMethodNotAllowedException;
@@ -60,86 +59,91 @@
   protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
     final String factoryClassName = getInitParameter(ODataServiceFactory.FACTORY_LABEL);
     if (factoryClassName == null) {
-      throw new ODataRuntimeException("config missing: org.apache.olingo.odata2.processor.factory");
+      throw new ODataRuntimeException("config missing: " + ODataServiceFactory.FACTORY_LABEL);
     }
+
+    // We have to create the Service Factory here because otherwise we do not have access to the error callback
+    ODataServiceFactory serviceFactory = createServiceFactory(req);
+
     String xHttpMethod = req.getHeader("X-HTTP-Method");
     String xHttpMethodOverride = req.getHeader("X-HTTP-Method-Override");
     if (xHttpMethod != null && xHttpMethodOverride != null) {
       if (!xHttpMethod.equalsIgnoreCase(xHttpMethodOverride)) {
-        ODataExceptionWrapper wrapper = new ODataExceptionWrapper(req);
+        ODataExceptionWrapper wrapper = new ODataExceptionWrapper(req, serviceFactory);
         createResponse(resp, wrapper.wrapInExceptionResponse(
             new ODataBadRequestException(ODataBadRequestException.AMBIGUOUS_XMETHOD)));
       }
     }
 
     if (req.getPathInfo() != null) {
-      handle(req, resp, xHttpMethod, xHttpMethodOverride);
+      handle(req, resp, xHttpMethod, xHttpMethodOverride, serviceFactory);
     } else {
-      handleRedirect(req, resp);
+      handleRedirect(req, resp, serviceFactory);
     }
   }
 
   private void handle(final HttpServletRequest req, final HttpServletResponse resp, final String xHttpMethod,
-      final String xHttpMethodOverride) throws IOException {
+      final String xHttpMethodOverride, ODataServiceFactory serviceFactory) throws IOException {
     String method = req.getMethod();
     if (ODataHttpMethod.GET.name().equals(method)) {
-      handleRequest(req, ODataHttpMethod.GET, resp);
+      handleRequest(req, ODataHttpMethod.GET, resp, serviceFactory);
     } else if (ODataHttpMethod.POST.name().equals(method)) {
       if (xHttpMethod == null && xHttpMethodOverride == null) {
-        handleRequest(req, ODataHttpMethod.POST, resp);
+        handleRequest(req, ODataHttpMethod.POST, resp, serviceFactory);
       } else if (xHttpMethod == null) {
         /* tunneling */
-        boolean methodHandled = handleHttpTunneling(req, resp, xHttpMethodOverride);
+        boolean methodHandled = handleHttpTunneling(req, resp, xHttpMethodOverride, serviceFactory);
         if (!methodHandled) {
-          createMethodNotAllowedResponse(req, ODataHttpException.COMMON, resp);
+          createMethodNotAllowedResponse(req, ODataHttpException.COMMON, resp, serviceFactory);
         }
       } else {
         /* tunneling */
-        boolean methodHandled = handleHttpTunneling(req, resp, xHttpMethod);
+        boolean methodHandled = handleHttpTunneling(req, resp, xHttpMethod, serviceFactory);
         if (!methodHandled) {
-          createNotImplementedResponse(req, ODataNotImplementedException.TUNNELING, resp);
+          createNotImplementedResponse(req, ODataNotImplementedException.TUNNELING, resp, serviceFactory);
         }
       }
 
     } else if (ODataHttpMethod.PUT.name().equals(method)) {
-      handleRequest(req, ODataHttpMethod.PUT, resp);
+      handleRequest(req, ODataHttpMethod.PUT, resp, serviceFactory);
     } else if (ODataHttpMethod.DELETE.name().equals(method)) {
-      handleRequest(req, ODataHttpMethod.DELETE, resp);
+      handleRequest(req, ODataHttpMethod.DELETE, resp, serviceFactory);
     } else if (ODataHttpMethod.PATCH.name().equals(method)) {
-      handleRequest(req, ODataHttpMethod.PATCH, resp);
+      handleRequest(req, ODataHttpMethod.PATCH, resp, serviceFactory);
     } else if (ODataHttpMethod.MERGE.name().equals(method)) {
-      handleRequest(req, ODataHttpMethod.MERGE, resp);
+      handleRequest(req, ODataHttpMethod.MERGE, resp, serviceFactory);
     } else if (HTTP_METHOD_HEAD.equals(method) || HTTP_METHOD_OPTIONS.equals(method)) {
-      createNotImplementedResponse(req, ODataNotImplementedException.COMMON, resp);
+      createNotImplementedResponse(req, ODataNotImplementedException.COMMON, resp, serviceFactory);
     } else {
-      createNotImplementedResponse(req, ODataHttpException.COMMON, resp);
+      createNotImplementedResponse(req, ODataHttpException.COMMON, resp, serviceFactory);
     }
   }
 
   private boolean handleHttpTunneling(final HttpServletRequest req, final HttpServletResponse resp,
-      final String xHttpMethod) throws IOException {
+      final String xHttpMethod, ODataServiceFactory serviceFactory) throws IOException {
     if (ODataHttpMethod.MERGE.name().equals(xHttpMethod)) {
-      handleRequest(req, ODataHttpMethod.MERGE, resp);
+      handleRequest(req, ODataHttpMethod.MERGE, resp, serviceFactory);
     } else if (ODataHttpMethod.PATCH.name().equals(xHttpMethod)) {
-      handleRequest(req, ODataHttpMethod.PATCH, resp);
+      handleRequest(req, ODataHttpMethod.PATCH, resp, serviceFactory);
     } else if (ODataHttpMethod.DELETE.name().equals(xHttpMethod)) {
-      handleRequest(req, ODataHttpMethod.DELETE, resp);
+      handleRequest(req, ODataHttpMethod.DELETE, resp, serviceFactory);
     } else if (ODataHttpMethod.PUT.name().equals(xHttpMethod)) {
-      handleRequest(req, ODataHttpMethod.PUT, resp);
+      handleRequest(req, ODataHttpMethod.PUT, resp, serviceFactory);
     } else if (ODataHttpMethod.GET.name().equals(xHttpMethod)) {
-      handleRequest(req, ODataHttpMethod.GET, resp);
+      handleRequest(req, ODataHttpMethod.GET, resp, serviceFactory);
     } else if (ODataHttpMethod.POST.name().equals(xHttpMethod)) {
-      handleRequest(req, ODataHttpMethod.POST, resp);
+      handleRequest(req, ODataHttpMethod.POST, resp, serviceFactory);
     } else if (HTTP_METHOD_HEAD.equals(xHttpMethod) || HTTP_METHOD_OPTIONS.equals(xHttpMethod)) {
-      createNotImplementedResponse(req, ODataNotImplementedException.COMMON, resp);
+      createNotImplementedResponse(req, ODataNotImplementedException.COMMON, resp, serviceFactory);
     } else {
-      createNotImplementedResponse(req, ODataNotImplementedException.COMMON, resp);
+      createNotImplementedResponse(req, ODataNotImplementedException.COMMON, resp, serviceFactory);
     }
     return true;
   }
 
   private void
-      handleRequest(final HttpServletRequest req, final ODataHttpMethod method, final HttpServletResponse resp)
+      handleRequest(final HttpServletRequest req, final ODataHttpMethod method, final HttpServletResponse resp,
+          ODataServiceFactory serviceFactory)
           throws IOException {
     try {
       final String pathSplitAsString = getInitParameter(ODataServiceFactory.PATH_SPLIT_LABEL);
@@ -148,7 +152,7 @@
         pathSplit = Integer.parseInt(pathSplitAsString);
       }
       if (req.getHeader(HttpHeaders.ACCEPT) != null && req.getHeader(HttpHeaders.ACCEPT).isEmpty()) {
-        createNotAcceptableResponse(req, ODataNotAcceptableException.COMMON, resp);
+        createNotAcceptableResponse(req, ODataNotAcceptableException.COMMON, resp, serviceFactory);
       }
       ODataRequest odataRequest = ODataRequest.method(method)
           .contentType(RestUtil.extractRequestContentType(req.getContentType()).toContentTypeString())
@@ -159,13 +163,13 @@
           .requestHeaders(RestUtil.extractHeaders(req))
           .body(req.getInputStream())
           .build();
-      ODataServiceFactory serviceFactory = createServiceFactory(req);
+
       ODataContextImpl context = new ODataContextImpl(odataRequest, serviceFactory);
       context.setParameter(ODataContext.HTTP_SERVLET_REQUEST_OBJECT, req);
 
       ODataService service = serviceFactory.createService(context);
-      if(service == null){
-        createServiceUnavailableResponse(req, ODataInternalServerErrorException.NOSERVICE, resp);
+      if (service == null) {
+        createServiceUnavailableResponse(req, ODataInternalServerErrorException.NOSERVICE, resp, serviceFactory);
       } else {
         context.setService(service);
         service.getProcessor().setContext(context);
@@ -174,8 +178,8 @@
         final ODataResponse odataResponse = requestHandler.handle(odataRequest);
         createResponse(resp, odataResponse);
       }
-    } catch (ODataException e) {
-      ODataExceptionWrapper wrapper = new ODataExceptionWrapper(req);
+    } catch (Exception e) {
+      ODataExceptionWrapper wrapper = new ODataExceptionWrapper(req, serviceFactory);
       createResponse(resp, wrapper.wrapInExceptionResponse(e));
     }
   }
@@ -194,7 +198,8 @@
     }
   }
 
-  private void handleRedirect(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
+  private void handleRedirect(final HttpServletRequest req, final HttpServletResponse resp,
+      ODataServiceFactory serviceFactory) throws IOException {
     String method = req.getMethod();
     if (ODataHttpMethod.GET.name().equals(method) ||
         ODataHttpMethod.POST.name().equals(method) ||
@@ -209,7 +214,7 @@
           .build();
       createResponse(resp, odataResponse);
     } else {
-      createNotImplementedResponse(req, ODataHttpException.COMMON, resp);
+      createNotImplementedResponse(req, ODataHttpException.COMMON, resp, serviceFactory);
     }
 
   }
@@ -239,26 +244,41 @@
     if (entity != null) {
       ServletOutputStream out = resp.getOutputStream();
       int curByte;
+      int contentLength = 0;
+
       if (entity instanceof InputStream) {
         while ((curByte = ((InputStream) entity).read()) != -1) {
+          contentLength++;
           out.write((char) curByte);
         }
         ((InputStream) entity).close();
       } else if (entity instanceof String) {
         String body = (String) entity;
-        out.write(body.getBytes("utf-8"));
+        final byte[] entityBytes = body.getBytes("utf-8");
+        out.write(entityBytes);
+        contentLength = entityBytes.length;
       }
 
+      if (response.getHeader(HttpHeaders.CONTENT_LENGTH) != null) {
+        // Override content length
+        try {
+          contentLength = Integer.parseInt(response.getHeader(HttpHeaders.CONTENT_LENGTH));
+        } catch (NumberFormatException e) {
+          // Ignore
+        }
+      }
+
+      resp.setContentLength(contentLength);
       out.flush();
       out.close();
     }
   }
 
   private void createNotImplementedResponse(final HttpServletRequest req, final MessageReference messageReference,
-      final HttpServletResponse resp) throws IOException {
+      final HttpServletResponse resp, ODataServiceFactory serviceFactory) throws IOException {
     // RFC 2616, 5.1.1: "An origin server SHOULD return the status code [...]
     // 501 (Not Implemented) if the method is unrecognized [...] by the origin server."
-    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req);
+    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req, serviceFactory);
     ODataResponse response =
         exceptionWrapper.wrapInExceptionResponse(new ODataNotImplementedException(messageReference));
 //    resp.setStatus(HttpStatusCodes.NOT_IMPLEMENTED.getStatusCode());
@@ -266,24 +286,24 @@
   }
 
   private void createMethodNotAllowedResponse(final HttpServletRequest req, final MessageReference messageReference,
-      final HttpServletResponse resp) throws IOException {
-    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req);
+      final HttpServletResponse resp, ODataServiceFactory serviceFactory) throws IOException {
+    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req, serviceFactory);
     ODataResponse response =
         exceptionWrapper.wrapInExceptionResponse(new ODataMethodNotAllowedException(messageReference));
     createResponse(resp, response);
   }
 
   private void createNotAcceptableResponse(final HttpServletRequest req, final MessageReference messageReference,
-      final HttpServletResponse resp) throws IOException {
-    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req);
+      final HttpServletResponse resp, ODataServiceFactory serviceFactory) throws IOException {
+    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req, serviceFactory);
     ODataResponse response =
         exceptionWrapper.wrapInExceptionResponse(new ODataNotAcceptableException(messageReference));
     createResponse(resp, response);
   }
 
   private void createServiceUnavailableResponse(HttpServletRequest req, MessageReference messageReference,
-      HttpServletResponse resp) throws IOException {
-    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req);
+      HttpServletResponse resp, ODataServiceFactory serviceFactory) throws IOException {
+    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req, serviceFactory);
     ODataResponse response =
         exceptionWrapper.wrapInExceptionResponse(new ODataInternalServerErrorException(messageReference));
     createResponse(resp, response);
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java
index 22644ab..48d606c 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java
@@ -107,33 +107,33 @@
 
   public static Map<String, List<String>> extractAllQueryParameters(final String queryString) {
     Map<String, List<String>> allQueryParameterMap = new HashMap<String, List<String>>();
-    
+
     if (queryString != null) {
       // At first the queryString will be decoded.
       List<String> queryParameters = Arrays.asList(Decoder.decode(queryString).split("\\&"));
       for (String param : queryParameters) {
         int indexOfEqualSign = param.indexOf("=");
-        
+
         if (indexOfEqualSign < 0) {
-          final List<String> parameterList = allQueryParameterMap.containsKey(param) ? allQueryParameterMap.get(param) 
+          final List<String> parameterList = allQueryParameterMap.containsKey(param) ? allQueryParameterMap.get(param)
               : new LinkedList<String>();
-         allQueryParameterMap.put(param, parameterList);
-          
+          allQueryParameterMap.put(param, parameterList);
+
           parameterList.add("");
         } else {
           final String key = param.substring(0, indexOfEqualSign);
-          final List<String> parameterList = allQueryParameterMap.containsKey(key) ? allQueryParameterMap.get(key) 
+          final List<String> parameterList = allQueryParameterMap.containsKey(key) ? allQueryParameterMap.get(key)
               : new LinkedList<String>();
-          
+
           allQueryParameterMap.put(key, parameterList);
           parameterList.add(param.substring(indexOfEqualSign + 1));
         }
       }
     }
-    
+
     return allQueryParameterMap;
   }
-  
+
   /*
    * Parses Accept-Language header. Returns a list sorted by quality parameter
    */
@@ -273,7 +273,14 @@
     while (pathInfoString.startsWith("/")) {
       pathInfoString = pathInfoString.substring(1);
     }
-    List<String> segments = Arrays.asList(pathInfoString.split("/", -1));
+    List<String> segments = null;
+    // EmptyStrings have to result in an empty list.
+    // Since split will always deliver an empty string back we have to do this manually
+    if (pathInfoString.isEmpty()) {
+      segments = new ArrayList<String>();
+    } else {
+      segments = Arrays.asList(pathInfoString.split("/", -1));
+    }
 
     if (pathSplit == 0) {
       precedingPathSegments = Collections.emptyList();
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
index f526920..7898a2c 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
@@ -101,7 +101,7 @@
     headers.put("content-type", "application/json");
     BatchChangeSetPart request = BatchChangeSetPart.method(PUT)
         .uri("Employees('2')")
-        .body("{\"Возра�т\":40}")
+        .body("{\"Возраст\":40}")
         .headers(headers)
         .contentId("111")
         .build();
@@ -121,8 +121,8 @@
     assertTrue(requestBody.contains("--batch_"));
     assertTrue(requestBody.contains("--changeset_"));
     assertTrue(requestBody.contains("PUT Employees('2') HTTP/1.1"));
-    assertTrue(requestBody.contains("{\"Возра�т\":40}"));
-    assertEquals(16, batchRequestStream.linesCount());
+    assertTrue(requestBody.contains("{\"Возраст\":40}"));
+    assertEquals(15, batchRequestStream.linesCount());
 
     String contentType = "multipart/mixed; boundary=" + BOUNDARY;
     BatchParser parser = new BatchParser(contentType, parseProperties, true);
@@ -162,7 +162,7 @@
     assertTrue(requestBody.contains("GET Employees HTTP/1.1"));
     assertTrue(requestBody.contains("POST Employees HTTP/1.1"));
     assertTrue(requestBody.contains(body));
-    assertEquals(25, batchRequestStream.linesCount());
+    assertEquals(24, batchRequestStream.linesCount());
 
     String contentType = "multipart/mixed; boundary=" + BOUNDARY;
     BatchParser parser = new BatchParser(contentType, parseProperties, true);
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterITTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterITTest.java
new file mode 100644
index 0000000..b153596
--- /dev/null
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterITTest.java
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * 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.odata2.core.batch;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.batch.BatchException;
+import org.apache.olingo.odata2.api.batch.BatchRequestPart;
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSet;
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart;
+import org.apache.olingo.odata2.api.client.batch.BatchPart;
+import org.apache.olingo.odata2.api.client.batch.BatchQueryPart;
+import org.apache.olingo.odata2.api.commons.HttpHeaders;
+import org.apache.olingo.odata2.api.ep.EntityProviderBatchProperties;
+import org.apache.olingo.odata2.api.processor.ODataRequest;
+import org.apache.olingo.odata2.core.PathInfoImpl;
+import org.apache.olingo.odata2.core.batch.v2.BatchParser;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class BatchRequestWriterITTest {
+  private static final String POST = "POST";
+  private static final String GET = "GET";
+  private static final String BOUNDARY = "batch_123";
+  private static final String CONTENT_TYPE = "multipart/mixed ;boundary=" + BOUNDARY;
+  private static final String SERVICE_ROOT = "http://localhost/odata/";
+  private static EntityProviderBatchProperties batchProperties;
+
+  @BeforeClass
+  public static void setProperties() throws URISyntaxException {
+    PathInfoImpl pathInfo = new PathInfoImpl();
+    pathInfo.setServiceRoot(new URI(SERVICE_ROOT));
+    batchProperties = EntityProviderBatchProperties.init().pathInfo(pathInfo).build();
+  }
+
+  @Test
+  public void testQueryPart() throws Exception {
+    List<BatchPart> batch = new ArrayList<BatchPart>();
+    Map<String, String> headers = new HashMap<String, String>();
+    headers.put("Accept", "application/json");
+    BatchPart request = BatchQueryPart.method(GET).uri("Employees").headers(headers).build();
+    batch.add(request);
+
+    BatchRequestWriter writer = new BatchRequestWriter();
+    InputStream stream = writer.writeBatchRequest(batch, BOUNDARY);
+
+    List<BatchRequestPart> parsedRequestParts = parseBatchRequest(stream);
+    assertEquals(1, parsedRequestParts.size());
+    BatchRequestPart part = parsedRequestParts.get(0);
+
+    assertFalse(part.isChangeSet());
+    assertEquals(1, part.getRequests().size());
+    ODataRequest oDataRequest = part.getRequests().get(0);
+    assertEquals("Employees", oDataRequest.getPathInfo().getODataSegments().get(0).getPath());
+    assertEquals("application/json", oDataRequest.getAcceptHeaders().get(0));
+  }
+
+  @Test
+  public void testChangeSet() throws Exception {
+    List<BatchPart> batch = new ArrayList<BatchPart>();
+    Map<String, String> headers = new HashMap<String, String>();
+    headers.put("Accept", "application/json");
+    BatchPart request = BatchQueryPart.method(GET).uri("Employees").headers(headers).contentId("000").build();
+    batch.add(request);
+
+    Map<String, String> changeSetHeaders = new HashMap<String, String>();
+    changeSetHeaders.put("content-type", "application/json");
+    String body = "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEA";
+    BatchChangeSetPart changeRequest = BatchChangeSetPart.method(POST)
+        .uri("Employees")
+        .body(body)
+        .headers(changeSetHeaders)
+        .contentId("111")
+        .build();
+    BatchChangeSet changeSet = BatchChangeSet.newBuilder().build();
+    changeSet.add(changeRequest);
+    batch.add(changeSet);
+    BatchRequestWriter writer = new BatchRequestWriter();
+    InputStream stream = writer.writeBatchRequest(batch, BOUNDARY);
+
+    final List<BatchRequestPart> parsedRequestParts = parseBatchRequest(stream);
+    assertEquals(2, parsedRequestParts.size());
+
+    // Get Request
+    final BatchRequestPart partGet = parsedRequestParts.get(0);
+    assertFalse(partGet.isChangeSet());
+    assertEquals(1, partGet.getRequests().size());
+    final ODataRequest oDataRequestGet = partGet.getRequests().get(0);
+    assertEquals("Employees", oDataRequestGet.getPathInfo().getODataSegments().get(0).getPath());
+    assertEquals("application/json", oDataRequestGet.getAcceptHeaders().get(0));
+
+    // Change set
+    final BatchRequestPart partChangeSet = parsedRequestParts.get(1);
+    assertTrue(partChangeSet.isChangeSet());
+    assertEquals(1, partChangeSet.getRequests().size());
+    final ODataRequest oDataRequestPost = partChangeSet.getRequests().get(0);
+    assertEquals("Employees", oDataRequestGet.getPathInfo().getODataSegments().get(0).getPath());
+    assertEquals("111", oDataRequestPost.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID));
+    assertEquals(body, streamToString(oDataRequestPost.getBody()));
+    assertEquals("application/json", oDataRequestPost.getRequestHeaderValue(HttpHeaders.CONTENT_TYPE));
+  }
+
+  @Test
+  public void testTwoChangeSets() throws Exception {
+    List<BatchPart> batch = new ArrayList<BatchPart>();
+
+    // Get request
+    Map<String, String> headers = new HashMap<String, String>();
+    headers.put("Accept", "application/json");
+    BatchPart request = BatchQueryPart.method(GET).uri("Employees").headers(headers).contentId("000").build();
+    batch.add(request);
+
+    Map<String, String> headerPostRequest = new HashMap<String, String>();
+    headerPostRequest.put("content-type", "application/json");
+
+    // Changeset 1
+    String bodyEmployee = "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEA";
+    BatchChangeSetPart postRequest = BatchChangeSetPart.method(POST)
+        .uri("Employees")
+        .body(bodyEmployee)
+        .headers(headerPostRequest)
+        .contentId("111")
+        .build();
+
+    String bodyEmployee2 = "TestString\r\n";
+    BatchChangeSetPart postRequest2 = BatchChangeSetPart.method(POST)
+        .uri("Employees")
+        .body(bodyEmployee2)
+        .headers(headerPostRequest)
+        .contentId("222")
+        .build();
+    BatchChangeSet changeSet = BatchChangeSet.newBuilder().build();
+    changeSet.add(postRequest);
+    changeSet.add(postRequest2);
+    batch.add(changeSet);
+
+    // Changeset 2
+    BatchChangeSet changeSet2 = BatchChangeSet.newBuilder().build();
+    postRequest2 = BatchChangeSetPart.method(POST)
+        .uri("Employees")
+        .body(bodyEmployee2)
+        .headers(headerPostRequest)
+        .contentId("222")
+        .build();
+    changeSet2.add(postRequest2);
+    postRequest = BatchChangeSetPart.method(POST)
+        .uri("Employees")
+        .body(bodyEmployee)
+        .headers(headerPostRequest)
+        .contentId("111")
+        .build();
+    changeSet2.add(postRequest);
+    batch.add(changeSet2);
+
+    // Write requests
+    BatchRequestWriter writer = new BatchRequestWriter();
+    InputStream stream = writer.writeBatchRequest(batch, BOUNDARY);
+    // Read requests
+    final List<BatchRequestPart> parsedRequestParts = parseBatchRequest(stream);
+    assertEquals(3, parsedRequestParts.size());
+
+    // Get request
+    final BatchRequestPart partGet = parsedRequestParts.get(0);
+    assertFalse(partGet.isChangeSet());
+    assertEquals(1, partGet.getRequests().size());
+    final ODataRequest oDataRequestGet = partGet.getRequests().get(0);
+    assertEquals("Employees", oDataRequestGet.getPathInfo().getODataSegments().get(0).getPath());
+    assertEquals("application/json", oDataRequestGet.getAcceptHeaders().get(0));
+
+    // Changeset 1
+    BatchRequestPart parsedChangeSet1 = parsedRequestParts.get(1);
+    assertTrue(parsedChangeSet1.isChangeSet());
+    assertEquals(2, parsedChangeSet1.getRequests().size());
+    ODataRequest oDataRequestPost1 = parsedChangeSet1.getRequests().get(0);
+    assertEquals("111", oDataRequestPost1.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID));
+    assertEquals(bodyEmployee, streamToString(oDataRequestPost1.getBody()));
+    assertEquals("application/json", oDataRequestPost1.getRequestHeaderValue(HttpHeaders.CONTENT_TYPE));
+
+    ODataRequest oDataRequestPost12 = parsedChangeSet1.getRequests().get(1);
+    assertEquals("222", oDataRequestPost12.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID));
+    assertEquals(bodyEmployee2, streamToString(oDataRequestPost12.getBody()));
+    assertEquals("application/json", oDataRequestPost12.getRequestHeaderValue(HttpHeaders.CONTENT_TYPE));
+
+    // Changeset 2
+    BatchRequestPart parsedChangeSet2 = parsedRequestParts.get(2);
+    assertTrue(parsedChangeSet2.isChangeSet());
+    assertEquals(2, parsedChangeSet2.getRequests().size());
+    ODataRequest oDataRequestPost21 = parsedChangeSet2.getRequests().get(0);
+    assertEquals("222", oDataRequestPost21.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID));
+    assertEquals(bodyEmployee2, streamToString(oDataRequestPost21.getBody()));
+    assertEquals("application/json", oDataRequestPost21.getRequestHeaderValue(HttpHeaders.CONTENT_TYPE));
+
+    ODataRequest oDataRequestPost22 = parsedChangeSet2.getRequests().get(1);
+    assertEquals("111", oDataRequestPost22.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID));
+    assertEquals(bodyEmployee, streamToString(oDataRequestPost22.getBody()));
+    assertEquals("application/json", oDataRequestPost22.getRequestHeaderValue(HttpHeaders.CONTENT_TYPE));
+  }
+
+  private List<BatchRequestPart> parseBatchRequest(InputStream batchRequest) throws BatchException {
+    final BatchParser parser = new BatchParser(CONTENT_TYPE, batchProperties, true);
+    return parser.parseBatchRequest(batchRequest);
+  }
+
+  private String streamToString(final InputStream in) throws IOException {
+    final BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(in));
+    final StringBuilder builder = new StringBuilder();
+    String line;
+
+    while ((line = reader.readLine()) != null) {
+      builder.append(line);
+    }
+
+    reader.close();
+    return builder.toString();
+  }
+}
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java
index e8f7cd4..395c2b8 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java
@@ -19,7 +19,6 @@
 package org.apache.olingo.odata2.core.batch;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
@@ -37,7 +36,6 @@
 import org.apache.olingo.odata2.api.client.batch.BatchQueryPart;
 import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings;
 import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
-import org.apache.olingo.odata2.testutil.helper.StringHelper;
 import org.junit.Test;
 
 public class BatchRequestWriterTest {
@@ -48,11 +46,6 @@
   private static final String BOUNDARY = "batch_123";
   private static final Object CRLF = "\r\n";
 
-  private void checkMimeHeaders(final String requestBody) {
-    assertTrue(requestBody.contains("Content-Type: application/http"));
-    assertTrue(requestBody.contains("Content-Transfer-Encoding: binary"));
-  }
-
   @Test
   public void testBatchQueryPart() throws BatchException, IOException {
     List<BatchPart> batch = new ArrayList<BatchPart>();
@@ -63,15 +56,22 @@
 
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
-
-    String requestBody = StringHelper.toStream(batchRequest).asString();
-    assertNotNull(batchRequest);
-    checkMimeHeaders(requestBody);
-
-    assertTrue(requestBody.contains("--batch_"));
-    assertTrue(requestBody.contains("GET Employees HTTP/1.1"));
-    checkHeaders(headers, requestBody);
-    assertEquals(9, StringHelper.countLines(requestBody));
+    
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(batchRequest));
+    List<Line> lines = reader.toList();
+    reader.close();
+    int index = 0;
+    
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("GET Employees HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Accept: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test
@@ -81,7 +81,7 @@
     headers.put("content-type", "application/json");
     BatchChangeSetPart request = BatchChangeSetPart.method(PUT)
         .uri("Employees('2')")
-        .body("{\"Возраст\":40}")
+        .body("{\"Age\":40}")
         .headers(headers)
         .contentId("111")
         .build();
@@ -92,15 +92,27 @@
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
 
-    String requestBody = StringHelper.inputStreamToString(batchRequest, true);
-    assertNotNull(batchRequest);
-    checkMimeHeaders(requestBody);
-    checkHeaders(headers, requestBody);
-
-    assertTrue(requestBody.contains("--batch_"));
-    assertTrue(requestBody.contains("--changeset_"));
-    assertTrue(requestBody.contains("PUT Employees('2') HTTP/1.1"));
-    assertTrue(requestBody.contains("{\"Возраст\":40}"));
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(batchRequest));
+    List<Line> lines = reader.toList();
+    reader.close();
+    int index = 0;
+     
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertTrue(lines.get(index++).toString().startsWith("Content-Type: multipart/mixed; boundary=changeset_"));
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Id: 111" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("PUT Employees('2') HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 10" + CRLF, lines.get(index++).toString());
+    assertEquals("content-type: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("{\"Age\":40}" + CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test
@@ -126,15 +138,37 @@
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
 
-    String requestBody = StringHelper.inputStreamToString(batchRequest, true);
-    assertNotNull(batchRequest);
-    checkMimeHeaders(requestBody);
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(batchRequest));
+    List<Line> lines = reader.toList();
+    reader.close();
+    int index = 0;
 
-    checkHeaders(headers, requestBody);
-    checkHeaders(changeSetHeaders, requestBody);
-    assertTrue(requestBody.contains("GET Employees HTTP/1.1"));
-    assertTrue(requestBody.contains("POST Employees HTTP/1.1"));
-    assertTrue(requestBody.contains(body));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Id: 000" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("GET Employees HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Accept: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertTrue(lines.get(index++).toString().startsWith("Content-Type: multipart/mixed; boundary=changeset_"));
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Id: 111" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("POST Employees HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 68" + CRLF, lines.get(index++).toString());
+    assertEquals("content-type: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals(body + CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test
@@ -150,12 +184,12 @@
 
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
-    
+
     BufferedReaderIncludingLineEndings reader =
         new BufferedReaderIncludingLineEndings(new InputStreamReader(batchRequest));
     List<Line> lines = reader.toList();
     reader.close();
-    
+
     int line = 0;
     assertEquals("--" + BOUNDARY + CRLF, lines.get(line++).toString());
     assertEquals("Content-Type: application/http" + CRLF, lines.get(line++).toString());
@@ -164,9 +198,9 @@
     assertEquals(CRLF, lines.get(line++).toString());
     assertEquals("GET Employees HTTP/1.1" + CRLF, lines.get(line++).toString());
     assertEquals("Accept: application/json" + CRLF, lines.get(line++).toString());
-    assertEquals(CRLF, lines.get(line++).toString());   // Belongs to the GET request [OData Protocol - 2.2.7.2.1]
-    
-    assertEquals(CRLF, lines.get(line++).toString());   // Belongs conceptually to the boundary [RFC 2046 - 5.1.1]
+    assertEquals(CRLF, lines.get(line++).toString()); // Belongs to the GET request [OData Protocol - 2.2.7.2.1]
+
+    assertEquals(CRLF, lines.get(line++).toString()); // Belongs conceptually to the boundary [RFC 2046 - 5.1.1]
     assertEquals("--" + BOUNDARY + CRLF, lines.get(line++).toString());
     assertEquals("Content-Type: application/http" + CRLF, lines.get(line++).toString());
     assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(line++).toString());
@@ -174,9 +208,9 @@
     assertEquals(CRLF, lines.get(line++).toString());
     assertEquals("GET Employees HTTP/1.1" + CRLF, lines.get(line++).toString());
     assertEquals("Accept: application/json" + CRLF, lines.get(line++).toString());
-    assertEquals(CRLF, lines.get(line++).toString());   // Belongs to the GET request [OData Protocol - 2.2.7.2.1]
-    
-    assertEquals(CRLF, lines.get(line++).toString());   // Belongs conceptually to the boundary [RFC 2046 - 5.1.1]
+    assertEquals(CRLF, lines.get(line++).toString()); // Belongs to the GET request [OData Protocol - 2.2.7.2.1]
+
+    assertEquals(CRLF, lines.get(line++).toString()); // Belongs conceptually to the boundary [RFC 2046 - 5.1.1]
     assertEquals("--" + BOUNDARY + "--", lines.get(line++).toString());
     assertEquals(19, lines.size());
   }
@@ -200,7 +234,7 @@
     changeSetHeaders = new HashMap<String, String>();
     changeSetHeaders.put("content-type", "application/json;odata=verbose");
     BatchChangeSetPart changeRequest2 = BatchChangeSetPart.method(PUT)
-        .uri("$/ManagerId")
+        .uri("$1/ManagerId")
         .body("{\"ManagerId\":1}")
         .headers(changeSetHeaders)
         .contentId("2")
@@ -211,16 +245,39 @@
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
 
-    String requestBody = StringHelper.inputStreamToString(batchRequest);
-    assertNotNull(batchRequest);
-    checkMimeHeaders(requestBody);
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(batchRequest));
+    List<Line> lines = reader.toList();
+    reader.close();
 
-    assertTrue(requestBody.contains("POST Employees('2') HTTP/1.1"));
-    assertTrue(requestBody.contains("PUT $/ManagerId HTTP/1.1"));
-    assertTrue(requestBody.contains(BatchHelper.HTTP_CONTENT_ID + ": 1"));
-    assertTrue(requestBody.contains(BatchHelper.HTTP_CONTENT_ID + ": 2"));
-    assertTrue(requestBody.contains(body));
-
+    int index = 0;
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertTrue(lines.get(index++).toString().startsWith("Content-Type: multipart/mixed; boundary=changeset_"));
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Id: 1" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("POST Employees('2') HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 68" + CRLF, lines.get(index++).toString());
+    assertEquals("content-type: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals(body + CRLF, lines.get(index++).toString());
+    
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Id: 2" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("PUT $1/ManagerId HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 15" + CRLF, lines.get(index++).toString());
+    assertEquals("content-type: application/json;odata=verbose" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("{\"ManagerId\":1}" + CRLF, lines.get(index++).toString());
+    
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test
@@ -254,22 +311,43 @@
 
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
+    
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(batchRequest));
+    List<Line> lines = reader.toList();
+    reader.close();
 
-    String requestBody = StringHelper.inputStreamToString(batchRequest);
-    assertNotNull(batchRequest);
-    checkMimeHeaders(requestBody);
-
-    assertTrue(requestBody.contains("POST Employees HTTP/1.1"));
-    assertTrue(requestBody.contains("PUT Employees('2')/ManagerId HTTP/1.1"));
-
-    assertTrue(requestBody.contains(body));
-
-  }
-
-  private void checkHeaders(final Map<String, String> headers, final String requestBody) {
-    for (Map.Entry<String, String> header : headers.entrySet()) {
-      assertTrue(requestBody.contains(header.getKey() + ": " + header.getValue()));
-    }
+    int index = 0;
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertTrue(lines.get(index++).toString().startsWith("Content-Type: multipart/mixed; boundary=changeset_"));
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("POST Employees HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 68" + CRLF, lines.get(index++).toString());
+    assertEquals("content-type: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals("content-Id: 111" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals(body + CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertTrue(lines.get(index++).toString().startsWith("Content-Type: multipart/mixed; boundary=changeset_"));
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("PUT Employees('2')/ManagerId HTTP/1.1" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 15" + CRLF, lines.get(index++).toString());
+    assertEquals("content-type: application/json;odata=verbose" + CRLF, lines.get(index++).toString());
+    assertEquals("content-Id: 222" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("{\"ManagerId\":1}" + CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test(expected = IllegalArgumentException.class)
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseParserTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseParserTest.java
index feaa50d..dacdb5a 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseParserTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseParserTest.java
@@ -64,6 +64,61 @@
       assertEquals("text/plain;charset=utf-8", response.getHeaders().get(HttpHeaders.CONTENT_TYPE));
       assertEquals("22", response.getHeaders().get("Content-length"));
       assertEquals("1", response.getContentId());
+      assertEquals("Frederic Fall MODIFIED", response.getBody());
+    }
+  }
+
+  @Test
+  public void testSimpleBatchResponseWithLinebreak() throws BatchException {
+    String getResponse = "--batch_123" + CRLF
+        + "Content-Type: application/http" + CRLF
+        + "Content-Transfer-Encoding: binary" + CRLF
+        + "Content-ID: 1" + CRLF
+        + CRLF
+        + "HTTP/1.1 200 OK" + CRLF
+        + "DataServiceVersion: 2.0" + CRLF
+        + "Content-Type: text/plain;charset=utf-8" + CRLF
+        + "Content-length: 24" + CRLF
+        + CRLF
+        + "Frederic Fall MODIFIED" + CRLF
+        + CRLF
+        + "--batch_123--";
+
+    InputStream in = new ByteArrayInputStream(getResponse.getBytes());
+    BatchParser parser = new BatchParser("multipart/mixed;boundary=batch_123", true);
+    List<BatchSingleResponse> responses = parser.parseBatchResponse(in);
+    for (BatchSingleResponse response : responses) {
+      assertEquals("200", response.getStatusCode());
+      assertEquals("OK", response.getStatusInfo());
+      assertEquals("text/plain;charset=utf-8", response.getHeaders().get(HttpHeaders.CONTENT_TYPE));
+      assertEquals("24", response.getHeaders().get("Content-length"));
+      assertEquals("1", response.getContentId());
+      assertEquals("Frederic Fall MODIFIED\r\n", response.getBody());
+    }
+  }
+
+  @Test
+  public void testNoContentResponse() throws Exception {
+    String responseContent =
+        "--ejjeeffe1\r\n" +
+            "Content-Type: application/http\r\n" +
+            "Content-Length: 96\r\n" +
+            "content-transfer-encoding: binary\r\n" +
+            "\r\n" +
+            "HTTP/1.1 204 No Content\r\n" +
+            "Content-Type: text/html\r\n" +
+            "dataserviceversion: 2.0\r\n" +
+            "\r\n" +
+            "\r\n" +
+            "--ejjeeffe1--\r\n";
+
+    InputStream in = new ByteArrayInputStream(responseContent.getBytes());
+    BatchParser parser = new BatchParser("multipart/mixed;boundary=ejjeeffe1", true);
+    List<BatchSingleResponse> responses = parser.parseBatchResponse(in);
+    for (BatchSingleResponse response : responses) {
+      assertEquals("204", response.getStatusCode());
+      assertEquals("No Content", response.getStatusInfo());
+      assertEquals("text/html", response.getHeaders().get(HttpHeaders.CONTENT_TYPE));
     }
   }
 
@@ -115,11 +170,74 @@
       assertEquals("1", response.getContentId());
     }
   }
+  
+  @Test
+  public void testResponseChangeSetBodyWithoutCRLF() throws BatchException {
+    String putResponse = "--batch_123" + CRLF
+        + "Content-Type: " + HttpContentType.MULTIPART_MIXED + ";boundary=changeset_12ks93js84d" + CRLF
+        + CRLF
+        + "--changeset_12ks93js84d" + CRLF
+        + "Content-Type: application/http" + CRLF
+        + "Content-Transfer-Encoding: binary" + CRLF
+        + "Content-ID: 1" + CRLF
+        + CRLF
+        + "HTTP/1.1 200 Ok" + CRLF
+        + "DataServiceVersion: 2.0" + CRLF
+        + "Content-Length: 19" + CRLF
+        + CRLF
+        + "TestBodyWithoutCRLF" + CRLF
+        + "--changeset_12ks93js84d--" + CRLF
+        + CRLF
+        + "--batch_123--";
 
+    InputStream in = new ByteArrayInputStream(putResponse.getBytes());
+    BatchParser parser = new BatchParser("multipart/mixed;boundary=batch_123", true);
+    List<BatchSingleResponse> responses = parser.parseBatchResponse(in);
+    for (BatchSingleResponse response : responses) {
+      assertEquals("200", response.getStatusCode());
+      assertEquals("Ok", response.getStatusInfo());
+      assertEquals("19", response.getHeader(HttpHeaders.CONTENT_LENGTH));
+      assertEquals("TestBodyWithoutCRLF", response.getBody());
+      assertEquals("1", response.getContentId());
+    }
+  }
+  
+  @Test
+  public void testResponseChangeSetBodyWithCRLF() throws BatchException {
+    String putResponse = "--batch_123" + CRLF
+        + "Content-Type: " + HttpContentType.MULTIPART_MIXED + ";boundary=changeset_12ks93js84d" + CRLF
+        + CRLF
+        + "--changeset_12ks93js84d" + CRLF
+        + "Content-Type: application/http" + CRLF
+        + "Content-Transfer-Encoding: binary" + CRLF
+        + "Content-ID: 1" + CRLF
+        + CRLF
+        + "HTTP/1.1 200 Ok" + CRLF
+        + "DataServiceVersion: 2.0" + CRLF
+        + "Content-Length: 18" + CRLF
+        + CRLF
+        + "TestBodyWithCRLF" + CRLF
+        + CRLF
+        + "--changeset_12ks93js84d--" + CRLF
+        + CRLF
+        + "--batch_123--";
+
+    InputStream in = new ByteArrayInputStream(putResponse.getBytes());
+    BatchParser parser = new BatchParser("multipart/mixed;boundary=batch_123", true);
+    List<BatchSingleResponse> responses = parser.parseBatchResponse(in);
+    for (BatchSingleResponse response : responses) {
+      assertEquals("200", response.getStatusCode());
+      assertEquals("Ok", response.getStatusInfo());
+      assertEquals("18", response.getHeader(HttpHeaders.CONTENT_LENGTH));
+      assertEquals("TestBodyWithCRLF" + CRLF, response.getBody());
+      assertEquals("1", response.getContentId());
+    }
+  }
+  
   @Test
   public void testResponseToChangeSetNoContentButContentLength() throws BatchException {
     String putResponse =
-            "--batch_123" + CRLF
+        "--batch_123" + CRLF
             + "Content-Type: " + HttpContentType.MULTIPART_MIXED + ";boundary=changeset_12ks93js84d" + CRLF
             + CRLF
             + "--changeset_12ks93js84d" + CRLF
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java
index 2f8b7f8..4076a25 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java
@@ -140,6 +140,6 @@
     StringHelper.Stream content = StringHelper.toStream(body);
     List<BatchSingleResponse> result = parser.parseBatchResponse(content.asStream());
     assertEquals(2, result.size());
-    assertEquals("Failing content:\n" + content.asString(), 20, content.linesCount());
+    assertEquals("Failing content:\n" + content.asString(), 19, content.linesCount());
   }
 }
\ No newline at end of file
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseWriterITTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseWriterITTest.java
new file mode 100644
index 0000000..df84f39
--- /dev/null
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseWriterITTest.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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.odata2.core.batch;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.odata2.api.batch.BatchResponsePart;
+import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse;
+import org.apache.olingo.odata2.api.commons.HttpHeaders;
+import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.processor.ODataResponse;
+import org.apache.olingo.odata2.core.batch.v2.BatchParser;
+import org.apache.olingo.odata2.core.commons.ContentType;
+import org.junit.Test;
+
+public class BatchResponseWriterITTest {
+
+  @Test
+  public void testSimpleRequest() throws Exception {
+    // Create batch response
+    List<BatchResponsePart> parts = new ArrayList<BatchResponsePart>();
+    ODataResponse response =
+        ODataResponse.entity("Walter Winter").status(HttpStatusCodes.OK).contentHeader("application/json").build();
+    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
+    responses.add(response);
+    parts.add(BatchResponsePart.responses(responses).changeSet(false).build());
+    BatchResponseWriter writer = new BatchResponseWriter();
+    ODataResponse batchResponse = writer.writeResponse(parts);
+
+    assertEquals(202, batchResponse.getStatus().getStatusCode());
+    assertNotNull(batchResponse.getEntity());
+    String body = (String) batchResponse.getEntity();
+    // Get boundary
+    int lineEndingIndex = body.indexOf("\r\n");
+    String boundary = body.substring(2, lineEndingIndex);
+
+    // Parse response and test outputs
+    final BatchParser parser = new BatchParser("multipart/mixed;boundary=" + boundary, true);
+    List<BatchSingleResponse> parserResponses = parser.parseBatchResponse(new ByteArrayInputStream(body.getBytes()));
+    for (BatchSingleResponse parserResponse : parserResponses) {
+      assertEquals("200", parserResponse.getStatusCode());
+      assertEquals("OK", parserResponse.getStatusInfo());
+      assertEquals("application/json", parserResponse.getHeaders().get(HttpHeaders.CONTENT_TYPE));
+      assertEquals("13", parserResponse.getHeaders().get(HttpHeaders.CONTENT_LENGTH));
+      assertEquals("Walter Winter", parserResponse.getBody());
+    }
+  }
+
+  @Test
+  public void testNoContent() throws Exception {
+    // Create batch response
+    List<BatchResponsePart> parts = new ArrayList<BatchResponsePart>();
+    ODataResponse response =
+        ODataResponse.status(HttpStatusCodes.NO_CONTENT).build();
+    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
+    responses.add(response);
+    parts.add(BatchResponsePart.responses(responses).changeSet(false).build());
+    BatchResponseWriter writer = new BatchResponseWriter();
+    ODataResponse batchResponse = writer.writeResponse(parts);
+
+    assertEquals(202, batchResponse.getStatus().getStatusCode());
+    assertNotNull(batchResponse.getEntity());
+    String body = (String) batchResponse.getEntity();
+    // Get boundary
+    int lineEndingIndex = body.indexOf("\r\n");
+    String boundary = body.substring(2, lineEndingIndex);
+
+    // Parse response and test outputs
+    final BatchParser parser = new BatchParser("multipart/mixed;boundary=" + boundary, true);
+    List<BatchSingleResponse> parserResponses = parser.parseBatchResponse(new ByteArrayInputStream(body.getBytes()));
+    for (BatchSingleResponse parserResponse : parserResponses) {
+      assertEquals("204", parserResponse.getStatusCode());
+      assertEquals("No Content", parserResponse.getStatusInfo());
+    }
+  }
+
+  @Test
+  public void testChangeSet() throws Exception {
+    List<BatchResponsePart> parts = new ArrayList<BatchResponsePart>();
+    ODataResponse response = ODataResponse.entity("Walter Winter")
+        .status(HttpStatusCodes.OK)
+        .contentHeader("application/json")
+        .build();
+    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
+    responses.add(response);
+    parts.add(BatchResponsePart.responses(responses).changeSet(false).build());
+
+    ODataResponse changeSetResponse =
+        ODataResponse.status(HttpStatusCodes.NO_CONTENT).header(BatchHelper.MIME_HEADER_CONTENT_ID, "1").build();
+    responses = new ArrayList<ODataResponse>(2);
+    ODataResponse changeSetResponseEntity =
+        ODataResponse.status(HttpStatusCodes.OK).contentHeader(ContentType.APPLICATION_JSON.toContentTypeString())
+            .header(BatchHelper.MIME_HEADER_CONTENT_ID, "2")
+            .entity("Test\r\n").build();
+    ODataResponse changeSetResponseEntity2 =
+        ODataResponse.status(HttpStatusCodes.OK).contentHeader(ContentType.APPLICATION_JSON.toContentTypeString())
+            .header(BatchHelper.MIME_HEADER_CONTENT_ID, "2")
+            .entity("Test\n").build();
+    ODataResponse changeSetResponseEntity3 =
+        ODataResponse.status(HttpStatusCodes.OK).contentHeader(ContentType.APPLICATION_JSON.toContentTypeString())
+            .header(BatchHelper.MIME_HEADER_CONTENT_ID, "2")
+            .entity("Test").build();
+    responses.add(changeSetResponse);
+    responses.add(changeSetResponseEntity);
+    responses.add(changeSetResponseEntity2);
+    responses.add(changeSetResponseEntity3);
+
+    parts.add(BatchResponsePart.responses(responses).changeSet(true).build());
+
+    BatchResponseWriter writer = new BatchResponseWriter();
+    ODataResponse batchResponse = writer.writeResponse(parts);
+
+    assertEquals(202, batchResponse.getStatus().getStatusCode());
+    assertNotNull(batchResponse.getEntity());
+    String body = (String) batchResponse.getEntity();
+
+    // Get boundary
+    int lineEndingIndex = body.indexOf("\r\n");
+    String boundary = body.substring(2, lineEndingIndex);
+
+    // Parse response and test outputs
+    final BatchParser parser = new BatchParser("multipart/mixed;boundary=" + boundary, true);
+    List<BatchSingleResponse> parserResponses = parser.parseBatchResponse(new ByteArrayInputStream(body.getBytes()));
+    assertEquals(5, parserResponses.size());
+
+    BatchSingleResponse parserResponse = parserResponses.get(0);
+    assertEquals("200", parserResponse.getStatusCode());
+    assertEquals("OK", parserResponse.getStatusInfo());
+    assertEquals("application/json", parserResponse.getHeaders().get(HttpHeaders.CONTENT_TYPE));
+    assertEquals("13", parserResponse.getHeaders().get(HttpHeaders.CONTENT_LENGTH));
+    assertEquals("Walter Winter", parserResponse.getBody());
+
+    parserResponse = parserResponses.get(1);
+    assertEquals("204", parserResponse.getStatusCode());
+    assertEquals("1", parserResponse.getContentId());
+    assertEquals("No Content", parserResponse.getStatusInfo());
+
+    parserResponse = parserResponses.get(2);
+    assertEquals("200", parserResponse.getStatusCode());
+    assertEquals("OK", parserResponse.getStatusInfo());
+    assertEquals("application/json", parserResponse.getHeaders().get(HttpHeaders.CONTENT_TYPE));
+    assertEquals("6", parserResponse.getHeaders().get(HttpHeaders.CONTENT_LENGTH));
+    assertEquals("Test\r\n", parserResponse.getBody());
+
+    parserResponse = parserResponses.get(3);
+    assertEquals("200", parserResponse.getStatusCode());
+    assertEquals("OK", parserResponse.getStatusInfo());
+    assertEquals("application/json", parserResponse.getHeaders().get(HttpHeaders.CONTENT_TYPE));
+    assertEquals("5", parserResponse.getHeaders().get(HttpHeaders.CONTENT_LENGTH));
+    assertEquals("Test\n", parserResponse.getBody());
+
+    parserResponse = parserResponses.get(4);
+    assertEquals("200", parserResponse.getStatusCode());
+    assertEquals("OK", parserResponse.getStatusInfo());
+    assertEquals("application/json", parserResponse.getHeaders().get(HttpHeaders.CONTENT_TYPE));
+    assertEquals("4", parserResponse.getHeaders().get(HttpHeaders.CONTENT_LENGTH));
+    assertEquals("Test", parserResponse.getBody());
+  }
+}
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseWriterTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseWriterTest.java
index cc58159..ecc5e93 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseWriterTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseWriterTest.java
@@ -19,11 +19,12 @@
 package org.apache.olingo.odata2.core.batch;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -31,10 +32,14 @@
 import org.apache.olingo.odata2.api.batch.BatchResponsePart;
 import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
 import org.apache.olingo.odata2.api.processor.ODataResponse;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.junit.Test;
 
 public class BatchResponseWriterTest {
 
+  private static final String CRLF = "\r\n";
+
   @Test
   public void testBatchResponse() throws BatchException, IOException {
     List<BatchResponsePart> parts = new ArrayList<BatchResponsePart>();
@@ -57,16 +62,35 @@
     assertEquals(202, batchResponse.getStatus().getStatusCode());
     assertNotNull(batchResponse.getEntity());
     String body = (String) batchResponse.getEntity();
-    
-    assertTrue(body.contains("--batch"));
-    assertTrue(body.contains("--changeset"));
-    assertTrue(body.contains("HTTP/1.1 200 OK"));
-    assertTrue(body.contains("Content-Type: application/http"));
-    assertTrue(body.contains("Content-Transfer-Encoding: binary"));
-    assertTrue(body.contains("Walter Winter"));
-    assertTrue(body.contains("multipart/mixed; boundary=changeset"));
-    assertTrue(body.contains("HTTP/1.1 204 No Content"));
 
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(new ByteArrayInputStream(body.getBytes())));
+    List<Line> lines = reader.toList();
+    reader.close();
+    int index = 0;
+
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("HTTP/1.1 200 OK" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Type: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 13" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("Walter Winter" + CRLF, lines.get(index++).toString());
+    
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertTrue(lines.get(index++).toString().startsWith("Content-Type: multipart/mixed; boundary=changeset_"));
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("HTTP/1.1 204 No Content" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test
@@ -83,15 +107,23 @@
     assertEquals(202, batchResponse.getStatus().getStatusCode());
     assertNotNull(batchResponse.getEntity());
     String body = (String) batchResponse.getEntity();
-
-    assertTrue(body.contains("--batch"));
-    assertFalse(body.contains("--changeset"));
-    assertTrue(body.contains("HTTP/1.1 200 OK" + "\r\n"));
-    assertTrue(body.contains("Content-Type: application/http" + "\r\n"));
-    assertTrue(body.contains("Content-Transfer-Encoding: binary" + "\r\n"));
-    assertTrue(body.contains("Walter Winter"));
-    assertFalse(body.contains("multipart/mixed; boundary=changeset"));
-
+    
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(new ByteArrayInputStream(body.getBytes())));
+    List<Line> lines = reader.toList();
+    reader.close();
+    int index = 0;
+    
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("HTTP/1.1 200 OK" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Type: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 13" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("Walter Winter" + CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test
@@ -108,15 +140,25 @@
     assertEquals(202, batchResponse.getStatus().getStatusCode());
     assertNotNull(batchResponse.getEntity());
     String body = (String) batchResponse.getEntity();
-    assertTrue(body.contains("--batch"));
-    assertTrue(body.contains("--changeset"));
-    assertTrue(body.indexOf("--changeset") != body.lastIndexOf("--changeset"));
-    assertFalse(body.contains("HTTP/1.1 200 OK" + "\r\n"));
-    assertTrue(body.contains("Content-Type: application/http" + "\r\n"));
-    assertTrue(body.contains("Content-Transfer-Encoding: binary" + "\r\n"));
-    assertTrue(body.contains("HTTP/1.1 204 No Content" + "\r\n"));
-    assertTrue(body.contains("Content-Type: multipart/mixed; boundary=changeset"));
-
+    
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(new ByteArrayInputStream(body.getBytes())));
+    List<Line> lines = reader.toList();
+    reader.close();
+    int index = 0;
+    
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertTrue(lines.get(index++).toString().startsWith("Content-Type: multipart/mixed; boundary=changeset_"));
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("HTTP/1.1 204 No Content" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--changeset"));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
   @Test
@@ -137,17 +179,25 @@
     assertEquals(202, batchResponse.getStatus().getStatusCode());
     assertNotNull(batchResponse.getEntity());
     String body = (String) batchResponse.getEntity();
+    
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(new ByteArrayInputStream(body.getBytes())));
+    List<Line> lines = reader.toList();
+    reader.close();
+    int index = 0;
 
-    String mimeHeader = "Content-Type: application/http" + "\r\n"
-        + "Content-Transfer-Encoding: binary" + "\r\n"
-        + "Content-Id: mimeHeaderContentId123" + "\r\n";
-
-    String requestHeader = "Content-Id: requestHeaderContentId123" + "\r\n"
-        + "Content-Type: application/json" + "\r\n"
-        + "Content-Length: 13" + "\r\n";
-
-    assertTrue(body.contains(mimeHeader));
-    assertTrue(body.contains(requestHeader));
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Id: mimeHeaderContentId123" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("HTTP/1.1 200 OK" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Id: requestHeaderContentId123" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Type: application/json" + CRLF, lines.get(index++).toString());
+    assertEquals("Content-Length: 13" + CRLF, lines.get(index++).toString());
+    assertEquals(CRLF, lines.get(index++).toString());
+    assertEquals("Walter Winter" + CRLF, lines.get(index++).toString());
+    assertTrue(lines.get(index++).toString().startsWith("--batch"));
   }
 
 }
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumerTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumerTest.java
index bab3454..da58246 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumerTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ep/consumer/XmlMetadataConsumerTest.java
@@ -176,7 +176,121 @@
       + "<EntityType Name= \"Photo\"><Key><PropertyRef Name=\"Id\"/></Key><Property Name=\"Id\" Type=\"Edm.Int32\" " +
       "Nullable=\"false\" MaxLength=\"Max\"/><Property Name=\"Name\" Type=\"Edm.Int32\" MaxLength=\"max\"/>"
       + "</EntityType></Schema></edmx:DataServices></edmx:Edmx>";
-
+  
+  @Test
+  public void testMetadataDokumentWithWhitepaces() throws Exception {
+    final String metadata = ""
+        + "<edmx:Edmx Version=\"1.0\" xmlns:edmx=\"" + Edm.NAMESPACE_EDMX_2007_06 + "\">"
+        + "   <edmx:DataServices m:DataServiceVersion=\"2.0\" xmlns:m=\"" + Edm.NAMESPACE_M_2007_08 + "\">"
+        + "       <Schema Namespace=\"" + NAMESPACE2 + "\" xmlns=\"" + Edm.NAMESPACE_EDM_2008_09 + "\">"
+        + "           <EntityType Name= \"Photo\">"
+        + "               <Key> "
+        + "                 <PropertyRef Name=\"Id\" />"
+        + "               </Key>"
+        + "               <Property Name=\"Id\" Type=\"Edm.Int16\" Nullable=\"false\" />"
+        + "               <MyAnnotation xmlns=\"http://company.com/odata\">   "
+        + "                 <child> value1</child>"
+        + "                 <child>value2</child>"
+        + "               </MyAnnotation>"
+        + "           </EntityType>"
+        + "       </Schema>"
+        + "  </edmx:DataServices>"
+        + "</edmx:Edmx>";
+    
+    XmlMetadataConsumer parser = new XmlMetadataConsumer();
+    XMLStreamReader reader = createStreamReader(metadata);
+    DataServices result = parser.readMetadata(reader, true);
+    
+    assertEquals(1, result.getSchemas().size());
+    List<EntityType> entityTypes = result.getSchemas().get(0).getEntityTypes();
+    assertEquals(1, entityTypes.size());
+    EntityType entityType = entityTypes.get(0);
+    List<AnnotationElement> annotationElements = entityType.getAnnotationElements();
+    assertEquals(1, annotationElements.size());
+    AnnotationElement annotationElement = annotationElements.get(0);
+    List<AnnotationElement> childElements = annotationElement.getChildElements();
+    assertEquals(2, childElements.size());
+    
+    assertEquals(" value1", childElements.get(0).getText());
+    assertEquals("value2", childElements.get(1).getText());
+  }
+  
+  @Test
+  public void testMetadataDokumentWithWhitepacesMultiline() throws Exception {
+    final String metadata = ""
+        + "<edmx:Edmx Version=\"1.0\" xmlns:edmx=\"" + Edm.NAMESPACE_EDMX_2007_06 + "\">"
+        + "   <edmx:DataServices m:DataServiceVersion=\"2.0\" xmlns:m=\"" + Edm.NAMESPACE_M_2007_08 + "\">"
+        + "       <Schema Namespace=\"" + NAMESPACE2 + "\" xmlns=\"" + Edm.NAMESPACE_EDM_2008_09 + "\">"
+        + "           <EntityType Name= \"Photo\">"
+        + "               <Key> "
+        + "                 <PropertyRef Name=\"Id\" />"
+        + "               </Key>"
+        + "               <Property Name=\"Id\" Type=\"Edm.Int16\" Nullable=\"false\" />"
+        + "               <MyAnnotation xmlns=\"http://company.com/odata\">   "
+        + "                 <child> value1\n"
+        + "                 long long long multiline attribute</child>"
+        + "                 <child>value2</child>"
+        + "               </MyAnnotation>"
+        + "           </EntityType>"
+        + "       </Schema>"
+        + "  </edmx:DataServices>"
+        + "</edmx:Edmx>";
+    
+    XmlMetadataConsumer parser = new XmlMetadataConsumer();
+    XMLStreamReader reader = createStreamReader(metadata);
+    DataServices result = parser.readMetadata(reader, true);
+    
+    assertEquals(1, result.getSchemas().size());
+    List<EntityType> entityTypes = result.getSchemas().get(0).getEntityTypes();
+    assertEquals(1, entityTypes.size());
+    EntityType entityType = entityTypes.get(0);
+    List<AnnotationElement> annotationElements = entityType.getAnnotationElements();
+    assertEquals(1, annotationElements.size());
+    AnnotationElement annotationElement = annotationElements.get(0);
+    List<AnnotationElement> childElements = annotationElement.getChildElements();
+    assertEquals(2, childElements.size());
+    
+    assertEquals(" value1\n" + 
+        "                 long long long multiline attribute", childElements.get(0).getText());
+    assertEquals("value2", childElements.get(1).getText());
+  }
+  
+  @Test
+  public void testMetadataDokumentWithWhitepaces2() throws Exception {
+    final String metadata = ""
+        + "<edmx:Edmx Version=\"1.0\" xmlns:edmx=\"" + Edm.NAMESPACE_EDMX_2007_06 + "\">"
+        + "   <edmx:DataServices m:DataServiceVersion=\"2.0\" xmlns:m=\"" + Edm.NAMESPACE_M_2007_08 + "\">"
+        + "       <Schema Namespace=\"" + NAMESPACE2 + "\" xmlns=\"" + Edm.NAMESPACE_EDM_2008_09 + "\">"
+        + "           <EntityType Name= \"Photo\">"
+        + "               <Key> "
+        + "                 <PropertyRef Name=\"Id\" />"
+        + "               </Key>"
+        + "               <Property Name=\"Id\" Type=\"Edm.Int16\" Nullable=\"false\" />"
+        + "               <MyAnnotation xmlns=\"http://company.com/odata\">   "
+        + "                 <child> value1"
+        + "</child></MyAnnotation>"
+        + "           </EntityType>"
+        + "       </Schema>"
+        + "  </edmx:DataServices>"
+        + "</edmx:Edmx>";
+    
+    XmlMetadataConsumer parser = new XmlMetadataConsumer();
+    XMLStreamReader reader = createStreamReader(metadata);
+    DataServices result = parser.readMetadata(reader, true);
+    
+    assertEquals(1, result.getSchemas().size());
+    List<EntityType> entityTypes = result.getSchemas().get(0).getEntityTypes();
+    assertEquals(1, entityTypes.size());
+    EntityType entityType = entityTypes.get(0);
+    List<AnnotationElement> annotationElements = entityType.getAnnotationElements();
+    assertEquals(1, annotationElements.size());
+    AnnotationElement annotationElement = annotationElements.get(0);
+    List<AnnotationElement> childElements = annotationElement.getChildElements();
+    assertEquals(1, childElements.size());
+    
+    assertEquals(" value1", childElements.get(0).getText());
+  }
+  
   @Test
   public void stringValueForMaxLegthFacet() throws Exception {
     XmlMetadataConsumer parser = new XmlMetadataConsumer();
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/servlet/ODataServletTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/servlet/ODataServletTest.java
index 1c74fc0..883ab96 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/servlet/ODataServletTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/servlet/ODataServletTest.java
@@ -18,16 +18,22 @@
  ******************************************************************************/
 package org.apache.olingo.odata2.core.servlet;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 
 import javax.servlet.GenericServlet;
 import javax.servlet.ServletConfig;
+import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.olingo.odata2.api.ODataServiceFactory;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
 import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.processor.ODataResponse;
+import org.apache.olingo.odata2.core.ODataResponseImpl;
 import org.apache.olingo.odata2.core.rest.ODataServiceFactoryImpl;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -91,6 +97,82 @@
     Mockito.verify(respMock).setHeader(HttpHeaders.LOCATION, "/");
   }
 
+  @Test
+  public void contentLengthCalculatedString() throws Exception {
+    final Method createResponse =
+        ODataServlet.class.getDeclaredMethod("createResponse", HttpServletResponse.class, ODataResponse.class);
+    createResponse.setAccessible(true);
+
+    final ODataServlet servlet = new ODataServlet();
+    final String content = "Test\r\n";
+    final ODataResponse response = ODataResponseImpl.status(HttpStatusCodes.OK).entity(content).build();
+    prepareResponseMockToWrite(respMock);
+    prepareServlet(servlet);
+
+    createResponse.invoke(servlet, respMock, response);
+    Mockito.verify(respMock).setContentLength(content.getBytes("utf-8").length);
+  }
+
+  @Test
+  public void contentLengthCalculatedStream() throws Exception {
+    final Method createResponse =
+        ODataServlet.class.getDeclaredMethod("createResponse", HttpServletResponse.class, ODataResponse.class);
+    createResponse.setAccessible(true);
+
+    final ODataServlet servlet = new ODataServlet();
+    final String content = "Test\r\n";
+
+    final ODataResponse response =
+        ODataResponseImpl.status(HttpStatusCodes.OK).entity(new ByteArrayInputStream(content.getBytes("utf-8")))
+            .build();
+    prepareResponseMockToWrite(respMock);
+    prepareServlet(servlet);
+
+    createResponse.invoke(servlet, respMock, response);
+    Mockito.verify(respMock).setContentLength(content.getBytes("utf-8").length);
+  }
+
+  @Test
+  public void contentLengthHeader() throws Exception {
+    final Method createResponse =
+        ODataServlet.class.getDeclaredMethod("createResponse", HttpServletResponse.class, ODataResponse.class);
+    createResponse.setAccessible(true);
+    final ODataServlet servlet = new ODataServlet();
+    final ODataResponse response =
+        ODataResponseImpl.status(HttpStatusCodes.OK).header(HttpHeaders.CONTENT_LENGTH, "15").entity("").build();
+    prepareResponseMockToWrite(respMock);
+    prepareServlet(servlet);
+
+    createResponse.invoke(servlet, respMock, response);
+
+    Mockito.verify(respMock).setHeader(HttpHeaders.CONTENT_LENGTH, "15");
+    Mockito.verify(respMock).setContentLength(15);
+  }
+
+  @Test
+  public void contentLengthHeaderInvalid() throws Exception {
+    final Method createResponse =
+        ODataServlet.class.getDeclaredMethod("createResponse", HttpServletResponse.class, ODataResponse.class);
+    createResponse.setAccessible(true);
+    final ODataServlet servlet = new ODataServlet();
+    final ODataResponse response =
+        ODataResponseImpl.status(HttpStatusCodes.OK).header(HttpHeaders.CONTENT_LENGTH, "ab").entity("Test").build();
+    prepareResponseMockToWrite(respMock);
+    prepareServlet(servlet);
+
+    createResponse.invoke(servlet, respMock, response);
+
+    Mockito.verify(respMock).setHeader(HttpHeaders.CONTENT_LENGTH, "ab");
+    Mockito.verify(respMock).setContentLength(4); // ||"Test"|| = 4
+  }
+
+  private void prepareResponseMockToWrite(final HttpServletResponse response) throws IOException {
+    Mockito.when(response.getOutputStream()).thenReturn(new ServletOutputStream() {
+      @Override
+      public void write(int b) throws IOException {}
+    });
+  }
+
   private void prepareRequest(final HttpServletRequest req, final String contextPath, final String servletPath) {
     Mockito.when(req.getMethod()).thenReturn("GET");
     Mockito.when(req.getContextPath()).thenReturn(contextPath);
@@ -110,5 +192,4 @@
     String factoryClassName = ODataServiceFactoryImpl.class.getName();
     Mockito.when(configMock.getInitParameter(ODataServiceFactory.FACTORY_LABEL)).thenReturn(factoryClassName);
   }
-
 }
diff --git a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/NullServiceTest.java b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/NullServiceTest.java
index 58b9aba..9f7ac08 100644
--- a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/NullServiceTest.java
+++ b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/NullServiceTest.java
@@ -48,7 +48,7 @@
 
   @Test
   public void nullServiceMustResultInODataResponse() throws Exception {
-    System.out.println("The following internal Server Error is wanted if this test doesnt fail!");
+    disableLogging();
     final HttpResponse response = executeGetRequest("$metadata");
     assertEquals(HttpStatusCodes.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusLine().getStatusCode());
 
diff --git a/odata2-lib/odata-testutil/src/main/java/org/apache/olingo/odata2/testutil/fit/FitErrorCallback.java b/odata2-lib/odata-testutil/src/main/java/org/apache/olingo/odata2/testutil/fit/FitErrorCallback.java
index fb8ff25..d63ff44 100644
--- a/odata2-lib/odata-testutil/src/main/java/org/apache/olingo/odata2/testutil/fit/FitErrorCallback.java
+++ b/odata2-lib/odata-testutil/src/main/java/org/apache/olingo/odata2/testutil/fit/FitErrorCallback.java
@@ -39,7 +39,6 @@
     if (context.getHttpStatus() == HttpStatusCodes.INTERNAL_SERVER_ERROR) {
       LOG.error("Internal Server Error", context.getException());
     }
-
     return EntityProvider.writeErrorDocument(context);
   }
 
diff --git a/odata2-spring/.springBeans b/odata2-spring/.springBeans
new file mode 100755
index 0000000..f95fac8
--- /dev/null
+++ b/odata2-spring/.springBeans
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<beansProjectDescription>
+	<version>1</version>
+	<pluginVersion><![CDATA[3.6.2.201410090854-RELEASE]]></pluginVersion>
+	<configSuffixes>
+		<configSuffix><![CDATA[xml]]></configSuffix>
+	</configSuffixes>
+	<enableImports><![CDATA[false]]></enableImports>
+	<configs>
+		<config>src/test/resources/spring/applicationContext.xml</config>
+	</configs>
+	<autoconfigs>
+	</autoconfigs>
+	<configSets>
+	</configSets>
+</beansProjectDescription>
diff --git a/odata2-spring/pom.xml b/odata2-spring/pom.xml
new file mode 100755
index 0000000..1a10bb9
--- /dev/null
+++ b/odata2-spring/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<artifactId>olingo-odata2-spring</artifactId>
+	<name>${project.artifactId}</name>
+
+	<parent>
+		<groupId>org.apache.olingo</groupId>
+		<artifactId>olingo-odata2-parent</artifactId>
+		<version>2.0.2-SNAPSHOT</version>
+	</parent>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-beans</artifactId>
+			<version>${spring.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>commons-logging</groupId>
+					<artifactId>commons-logging</artifactId>
+				</exclusion>
+			</exclusions>
+			<optional>true</optional>
+		</dependency>
+
+		<dependency>
+			<groupId>${project.groupId}</groupId>
+			<version>${project.version}</version>
+			<artifactId>olingo-odata2-core</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.cxf</groupId>
+			<artifactId>cxf-rt-frontend-jaxrs</artifactId>
+			<version>${cxf.version}</version>
+		</dependency>
+
+		<!-- Test dependencies -->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-context</artifactId>
+			<version>${spring.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-web</artifactId>
+			<version>${spring.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-test</artifactId>
+			<version>${spring.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>servlet-api</artifactId>
+			<version>2.5</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-logging</groupId>
+			<artifactId>commons-logging</artifactId>
+			<version>1.1.3</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoNamespaceHandler.java b/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoNamespaceHandler.java
new file mode 100755
index 0000000..9014733
--- /dev/null
+++ b/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoNamespaceHandler.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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.odata2.spring;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+public class OlingoNamespaceHandler extends NamespaceHandlerSupport {
+
+  @Override
+  public void init() {
+    registerBeanDefinitionParser("server", new OlingoServerDefinitionParser());
+  }
+}
diff --git a/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoRootLocator.java b/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoRootLocator.java
new file mode 100644
index 0000000..82442d6
--- /dev/null
+++ b/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoRootLocator.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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.odata2.spring;
+
+import org.apache.olingo.odata2.api.ODataServiceFactory;
+import org.apache.olingo.odata2.core.rest.ODataRootLocator;
+
+import javax.ws.rs.Path;
+
+/**
+ * Default OData root locator responsible to handle the whole path and delegate all calls to a sub locator:<p>
+ * <code>/{odata path} e.g. http://host:port/webapp/odata.svc/$metadata</code><br>
+ * All path segments defined by a servlet mapping belong to the odata uri.
+ * </p>
+ * This behavior can be changed:<p>
+ * <code>/{custom path}{odata path} e.g. http://host:port/webapp/bmw/odata.svc/$metadata</code><br>
+ * The first segment defined by a servlet mapping belong to customer context and the following segments are OData
+ * specific.
+ * </p>
+ *
+ */
+@Path("/")
+public class OlingoRootLocator extends ODataRootLocator {
+
+  // These next two members are exposed so that they can be injected with Spring
+  private ODataServiceFactory serviceFactory;
+  private int pathSplit = 0;
+
+  @Override
+  public ODataServiceFactory getServiceFactory() {
+    return serviceFactory;
+  }
+
+  public void setServiceFactory(ODataServiceFactory serviceFactory) {
+    this.serviceFactory = serviceFactory;
+  }
+
+  @Override
+  public int getPathSplit() {
+    return pathSplit;
+  }
+
+  public void setPathSplit(int pathSplit) {
+    this.pathSplit = pathSplit;
+  }
+}
diff --git a/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoServerDefinitionParser.java b/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoServerDefinitionParser.java
new file mode 100755
index 0000000..c517ad4
--- /dev/null
+++ b/odata2-spring/src/main/java/org/apache/olingo/odata2/spring/OlingoServerDefinitionParser.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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.odata2.spring;
+
+import org.apache.cxf.jaxrs.spring.JAXRSServerFactoryBeanDefinitionParser;
+import org.apache.olingo.odata2.core.rest.ODataExceptionMapperImpl;
+import org.apache.olingo.odata2.core.rest.app.ODataApplication;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+/**
+ *
+ */
+public class OlingoServerDefinitionParser extends JAXRSServerFactoryBeanDefinitionParser {
+
+  protected static final String OLINGO_ROOT_LOCATOR = "OlingoRootLocator";
+  protected static final String OLINGO_ODATA_PROVIDER = "OlingoODataProvider";
+  protected static final String OLINGO_ODATA_EXCEPTION_HANDLER = "OlingoODataExceptionHandler";
+  protected static final String SERVICE_FACTORY = "serviceFactory";
+  protected static final String SERVICE_BEANS = "serviceBeans";
+  protected static final String ID = "id";
+  protected static final String FACTORY = "factory";
+  protected static final String PATH_SPLIT = "pathSplit";
+
+  public OlingoServerDefinitionParser() {
+    super();
+    setBeanClass(SpringJAXRSServerFactoryBean.class);
+  }
+
+  @Override
+  protected void mapAttribute(BeanDefinitionBuilder bean, Element e, String name, String val) {
+    if (ID.equals(name) || "address".equals(name)) {
+      mapToProperty(bean, name, val);
+    }
+  }
+
+  @Override
+  protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder bean) {
+    super.doParse(element, parserContext, bean);
+
+    if (!parserContext.getRegistry().containsBeanDefinition(OLINGO_ODATA_EXCEPTION_HANDLER)) {
+      AbstractBeanDefinition definition =
+          BeanDefinitionBuilder.genericBeanDefinition(ODataExceptionMapperImpl.class).getBeanDefinition();
+      definition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
+      BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, OLINGO_ODATA_EXCEPTION_HANDLER, new String[0]);
+      registerBeanDefinition(holder, parserContext.getRegistry());
+    }
+
+    if (!parserContext.getRegistry().containsBeanDefinition(OLINGO_ODATA_PROVIDER)) {
+      AbstractBeanDefinition definition =
+          BeanDefinitionBuilder.genericBeanDefinition(ODataApplication.MyProvider.class).getBeanDefinition();
+      definition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
+      BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, OLINGO_ODATA_PROVIDER, new String[0]);
+      registerBeanDefinition(holder, parserContext.getRegistry());
+    }
+
+    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(OlingoRootLocator.class);
+    builder.setScope(BeanDefinition.SCOPE_PROTOTYPE);
+    builder.addPropertyReference(SERVICE_FACTORY, element.getAttribute(FACTORY));
+    if (element.hasAttribute(PATH_SPLIT)) {
+      builder.addPropertyValue(PATH_SPLIT, element.getAttribute(PATH_SPLIT));
+    }
+    AbstractBeanDefinition definition = builder.getBeanDefinition();
+    BeanDefinitionHolder holder = new BeanDefinitionHolder(definition,
+        OLINGO_ROOT_LOCATOR + "-" + element.getAttribute(ID) + "-" + element.getAttribute(FACTORY));
+    registerBeanDefinition(holder, parserContext.getRegistry());
+
+    ManagedList<BeanDefinition> services = new ManagedList<BeanDefinition>(3);
+    services.add(definition);
+    services.add(parserContext.getRegistry().getBeanDefinition(OLINGO_ODATA_EXCEPTION_HANDLER));
+    services.add(parserContext.getRegistry().getBeanDefinition(OLINGO_ODATA_PROVIDER));
+    bean.addPropertyValue(SERVICE_BEANS, services);
+  }
+
+}
diff --git a/odata2-spring/src/main/resources/META-INF/spring.handlers b/odata2-spring/src/main/resources/META-INF/spring.handlers
new file mode 100755
index 0000000..dadb52f
--- /dev/null
+++ b/odata2-spring/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1,8 @@
+<!-- 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. -->
+http\://www.apache.org/olingo/odata2/spring/odata=org.apache.olingo.odata2.spring.OlingoNamespaceHandler
diff --git a/odata2-spring/src/main/resources/META-INF/spring.schemas b/odata2-spring/src/main/resources/META-INF/spring.schemas
new file mode 100755
index 0000000..1a2a280
--- /dev/null
+++ b/odata2-spring/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,8 @@
+<!-- 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. -->
+http\://www.apache.org/olingo/odata2/spring/odata.xsd=schema/olingo.xsd
diff --git a/odata2-spring/src/main/resources/schema/olingo.xsd b/odata2-spring/src/main/resources/schema/olingo.xsd
new file mode 100755
index 0000000..436eb74
--- /dev/null
+++ b/odata2-spring/src/main/resources/schema/olingo.xsd
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<xsd:schema xmlns="http://www.apache.org/olingo/odata2/spring/odata"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
+	targetNamespace="http://www.apache.org/olingo/odata2/spring/odata"
+	elementFormDefault="unqualified">
+
+	<xsd:import namespace="http://www.springframework.org/schema/beans"
+		schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd" />
+
+	<xsd:element name="server">
+		<xsd:complexType>
+			<xsd:complexContent>
+				<xsd:extension base="beans:identifiedType">
+					<xsd:attribute name="address" type="xsd:string" use="required" />
+					<xsd:attribute name="factory" type="xsd:string" use="required" />
+					<xsd:attribute name="pathSplit" type="xsd:int" use="optional" />
+				</xsd:extension>
+			</xsd:complexContent>
+		</xsd:complexType>
+	</xsd:element>
+</xsd:schema>
\ No newline at end of file
diff --git a/odata2-spring/src/test/java/org/apache/olingo/odata2/spring/SpringNamespaceHandlerTest.java b/odata2-spring/src/test/java/org/apache/olingo/odata2/spring/SpringNamespaceHandlerTest.java
new file mode 100755
index 0000000..089cfe3
--- /dev/null
+++ b/odata2-spring/src/test/java/org/apache/olingo/odata2/spring/SpringNamespaceHandlerTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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.odata2.spring;
+
+import org.apache.cxf.jaxrs.spring.JAXRSServerFactoryBeanDefinitionParser.SpringJAXRSServerFactoryBean;
+import org.apache.olingo.odata2.core.rest.ODataExceptionMapperImpl;
+import org.apache.olingo.odata2.core.rest.app.ODataApplication;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:spring/applicationContext.xml")
+@WebAppConfiguration
+public class SpringNamespaceHandlerTest {
+
+  @Autowired
+  private ApplicationContext appCtx;
+
+  @Test
+  public void testSuccessfullyCreated() {
+    assertTrue(appCtx.containsBean("testServer"));
+
+    assertTrue(appCtx.containsBean(OlingoServerDefinitionParser.OLINGO_ODATA_EXCEPTION_HANDLER));
+    assertTrue(appCtx.containsBean(OlingoServerDefinitionParser.OLINGO_ODATA_PROVIDER));
+
+    assertEquals(ODataExceptionMapperImpl.class, appCtx.getType("OlingoODataExceptionHandler"));
+    assertEquals(ODataApplication.MyProvider.class, appCtx.getType("OlingoODataProvider"));
+
+    String rootLocatorName = "OlingoRootLocator-testServer-serviceFactory";
+    assertTrue(appCtx.containsBean(rootLocatorName));
+    assertEquals(OlingoRootLocator.class, appCtx.getType(rootLocatorName));
+
+    SpringJAXRSServerFactoryBean server = appCtx.getBean("testServer", SpringJAXRSServerFactoryBean.class);
+    assertEquals("/service.svc", server.getAddress());
+  }
+
+  @Test
+  public void testCorrectFactoryAndPathSplit() {
+    String rootLocatorName = "OlingoRootLocator-testServer-serviceFactory";
+    OlingoRootLocator rootLocator = appCtx.getBean(rootLocatorName, OlingoRootLocator.class);
+    assertNotNull(rootLocator.getServiceFactory());
+    assertSame(appCtx.getBean("serviceFactory"), rootLocator.getServiceFactory());
+    assertEquals(3, rootLocator.getPathSplit());
+  }
+}
diff --git a/odata2-spring/src/test/java/org/apache/olingo/odata2/spring/TestFactory.java b/odata2-spring/src/test/java/org/apache/olingo/odata2/spring/TestFactory.java
new file mode 100755
index 0000000..746a03b
--- /dev/null
+++ b/odata2-spring/src/test/java/org/apache/olingo/odata2/spring/TestFactory.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.odata2.spring;
+
+import org.apache.olingo.odata2.api.ODataService;
+import org.apache.olingo.odata2.api.ODataServiceFactory;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.api.processor.ODataContext;
+
+/**
+ * Empty service factory, to be used for testing that the namespace handler and server definition
+ * parser work correctly.
+ *
+ * @author Lior Okman <lior.okman@sap.com>
+ */
+public class TestFactory extends ODataServiceFactory {
+
+  @Override
+  public ODataService createService(ODataContext ctx) throws ODataException {
+    return null;
+  }
+
+}
diff --git a/odata2-spring/src/test/resources/spring/applicationContext.xml b/odata2-spring/src/test/resources/spring/applicationContext.xml
new file mode 100755
index 0000000..31e9cd8
--- /dev/null
+++ b/odata2-spring/src/test/resources/spring/applicationContext.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:odata="http://www.apache.org/olingo/odata2/spring/odata"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+		http://www.apache.org/olingo/odata2/spring/odata http://www.apache.org/olingo/odata2/spring/odata.xsd
+		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
+
+	<!-- Make @Autowire work -->
+	<context:annotation-config />
+
+	<!-- Register the test factory -->
+	<bean id="serviceFactory" class="org.apache.olingo.odata2.spring.TestFactory" />
+
+	<!-- This is what is actually being tested -->
+	<odata:server id="testServer" address="/service.svc" factory="serviceFactory" pathSplit="3"/>
+
+	<!-- In order to be useful in a real environment, Apache CXF needs to be configured correctly as well.
+	     This context file doesn't handle this, since the unit test only tests that the namespace handler
+	     works
+	-->
+</beans>
diff --git a/pom.xml b/pom.xml
index fbe059e..9ffc517 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,6 +72,8 @@
 
 		<version.eclipselink>2.5.1</version.eclipselink>
 		<version.javax.persistence>2.0.5</version.javax.persistence>
+
+		<spring.version>3.2.12.RELEASE</spring.version>
 	</properties>
 
 	<modules>
@@ -348,6 +350,8 @@
 										<exclude>**/NOTICE</exclude>
 										<exclude>**/DEPENDENCIES</exclude>
 										<exclude>**/goal.txt</exclude>
+										<exclude>**/target/**</exclude>
+										<exclude>**/*.iml</exclude>
 									</excludes>
 								</configuration>
 							</execution>