[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>