You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2013/12/05 13:30:55 UTC

[02/52] Initial EdmAnnotation contribution

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/AnnotationProcessor.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/AnnotationProcessor.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/AnnotationProcessor.java
new file mode 100644
index 0000000..b97d897
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/AnnotationProcessor.java
@@ -0,0 +1,439 @@
+/*******************************************************************************
+ * 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.annotation.processor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.api.processor.ODataResponse;
+import org.apache.olingo.odata2.api.processor.ODataSingleProcessor;
+import org.apache.olingo.odata2.api.uri.KeyPredicate;
+import org.apache.olingo.odata2.api.uri.info.DeleteUriInfo;
+import org.apache.olingo.odata2.api.uri.info.GetEntitySetUriInfo;
+import org.apache.olingo.odata2.api.uri.info.GetEntityUriInfo;
+import org.apache.olingo.odata2.api.uri.info.PostUriInfo;
+import org.apache.olingo.odata2.api.uri.info.PutMergePatchUriInfo;
+import org.apache.olingo.odata2.api.annotation.edm.ds.EntityCreate;
+import org.apache.olingo.odata2.api.annotation.edm.ds.EntityDataSource;
+import org.apache.olingo.odata2.api.annotation.edm.ds.EntityDelete;
+import org.apache.olingo.odata2.api.annotation.edm.ds.EntityRead;
+import org.apache.olingo.odata2.api.annotation.edm.ds.EntitySetRead;
+import org.apache.olingo.odata2.api.annotation.edm.ds.EntityUpdate;
+import org.apache.olingo.odata2.api.processor.ODataContext;
+import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
+import org.apache.olingo.odata2.core.annotation.edm.AnnotationHelper;
+import org.apache.olingo.odata2.core.annotation.edm.ClassHelper;
+import org.apache.olingo.odata2.core.annotation.processor.json.EdmAnnotationSerializer;
+import org.apache.olingo.odata2.core.annotation.processor.json.JsonConsumer;
+
+/**
+ *
+ */
+public class AnnotationProcessor extends ODataSingleProcessor {
+
+  private static final AnnotationHelper ANNOTATION_HELPER = new AnnotationHelper();
+
+  private final List<Class<?>> foundClasses;
+
+  private final Map<String, DataSourceHolder> dataSources = new HashMap<String, DataSourceHolder>();
+  private ODataContext odataContext;
+
+  public AnnotationProcessor(ODataContext context, String packageToScan) {
+    odataContext = context;
+
+    foundClasses = ClassHelper.loadClasses(packageToScan, new ClassHelper.ClassValidator() {
+      @Override
+      public boolean isClassValid(Class<?> c) {
+        return null != c.getAnnotation(EntityDataSource.class);
+      }
+    });
+
+    init();
+  }
+
+  private void init() {
+    for (Class<?> clz : foundClasses) {
+      DataSourceHolder dhs = new DataSourceHolder(clz);
+      dataSources.put(dhs.getEntityName(), dhs);
+    }
+  }
+
+  @Override
+  public ODataResponse readEntity(GetEntityUriInfo uriInfo, String contentType) throws ODataException {
+    final String name = uriInfo.getTargetType().getName();
+
+    List<KeyPredicate> keys = uriInfo.getKeyPredicates();
+    DataSourceHolder holder = dataSources.get(name);
+    if (holder != null) {
+      Object result = holder.readEntity(keys);
+      if (result != null) {
+        return createODataResponse(result, HttpStatusCodes.OK);
+      }
+    }
+
+    return ODataResponse.status(HttpStatusCodes.NOT_FOUND).build();
+  }
+
+  @Override
+  public ODataResponse readEntitySet(GetEntitySetUriInfo uriInfo, String contentType) throws ODataException {
+    final String name = uriInfo.getTargetType().getName();
+
+    DataSourceHolder holder = dataSources.get(name);
+    if (holder != null) {
+      Object result = holder.readEntitySet();
+      if (result != null) {
+        return createODataResponse(result, HttpStatusCodes.OK);
+      }
+    }
+
+    return ODataResponse.status(HttpStatusCodes.NOT_FOUND).build();
+  }
+
+  @Override
+  public ODataResponse createEntity(PostUriInfo uriInfo, InputStream content,
+          String requestContentType, String contentType) throws ODataException {
+    final String name = uriInfo.getTargetType().getName();
+
+    DataSourceHolder dataSource = dataSources.get(name);
+    if (dataSource != null) {
+      Object instance = createInstanceFromContent(dataSource, content);
+
+      Object result = dataSource.createEntity(instance);
+      return createODataResponse(result, HttpStatusCodes.CREATED);
+    }
+
+    return ODataResponse.status(HttpStatusCodes.BAD_REQUEST).build();
+  }
+
+  @Override
+  public ODataResponse deleteEntity(DeleteUriInfo uriInfo, String contentType) throws ODataException {
+    final String name = uriInfo.getTargetType().getName();
+
+    DataSourceHolder holder = dataSources.get(name);
+    if (holder != null) {
+      List<KeyPredicate> keys = uriInfo.getKeyPredicates();
+      Object result = holder.deleteEntity(keys);
+      if (result != null) {
+        return ODataResponse.status(HttpStatusCodes.NO_CONTENT).build();
+      }
+    }
+
+    return ODataResponse.status(HttpStatusCodes.NOT_FOUND).build();
+  }
+
+  private ODataResponse createODataResponse(Object result, HttpStatusCodes statusCode) throws ODataException {
+//    StringBuilder resultAsString = new StringBuilder("{\"d\":");
+//    if (result != null) {
+//      resultAsString.append(result.toString());
+//    } else {
+//      resultAsString.append("NULL");
+//    }
+//    resultAsString.append("}");
+    EdmAnnotationSerializer jsonSerializer = 
+            new EdmAnnotationSerializer(odataContext.getPathInfo().getServiceRoot().toASCIIString());
+    InputStream resultAsString = jsonSerializer.serialize(result);
+    return ODataResponse.status(statusCode)
+            .contentHeader("application/json")
+            .entity(resultAsString).build();
+  }
+
+  private Object createInstanceFromContent(DataSourceHolder dataSource, InputStream content) 
+          throws ODataException {
+    try {
+      Object instance = dataSource.createEntityInstance();
+      Map<String, FieldHolder> propName2Field = extractPropertyFields(dataSource.entityTypeClass);
+      Map<String, String> contentAsMap = JsonConsumer.readContent(content);
+      Set<Map.Entry<String, String>> contentEntries = contentAsMap.entrySet();
+      for (Map.Entry<String, String> entry : contentEntries) {
+        FieldHolder fh = propName2Field.get(entry.getKey());
+        if (fh != null) {
+          fh.set(instance, entry.getValue());
+        }
+      }
+      return instance;      
+    } catch (IOException e) {
+      throw new ODataRuntimeException("Unexpected IOException with message: " + e.getMessage());
+    }
+  }
+
+  @Override
+  public ODataResponse updateEntity(PutMergePatchUriInfo uriInfo, InputStream content, 
+          String requestContentType, boolean merge, String contentType) throws ODataException {
+    final String name = uriInfo.getTargetType().getName();
+
+    DataSourceHolder dataSource = dataSources.get(name);
+    if (dataSource != null) {
+      Object instance = createInstanceFromContent(dataSource, content);
+
+      Object result = dataSource.updateEntity(instance);
+      return createODataResponse(result, HttpStatusCodes.OK);
+    }
+
+    return ODataResponse.status(HttpStatusCodes.BAD_REQUEST).build();
+  }
+
+  private Map<String, FieldHolder> extractPropertyFields(Class<?> typeClass) {
+    if (typeClass == null) {
+      return Collections.emptyMap();
+    }
+    EdmEntityType type = typeClass.getAnnotation(EdmEntityType.class);
+    if (type == null) {
+      return Collections.emptyMap();
+    }
+
+    //
+    Map<String, FieldHolder> name2Fields = new HashMap<String, FieldHolder>();
+    Field[] fields = typeClass.getDeclaredFields();
+    for (Field field : fields) {
+      FieldHolder fh = FieldHolder.create(field);
+      name2Fields.put(fh.propertyName, fh);
+    }
+
+    //
+    Class<?> superClass = typeClass.getSuperclass();
+    if (superClass != null && superClass.getAnnotation(EdmEntityType.class) != null) {
+      name2Fields.putAll(extractPropertyFields(superClass));
+    }
+    //
+
+    return name2Fields;
+  }
+
+  /**
+   *
+   */
+  static final class FieldHolder {
+
+    final String propertyName;
+    final Field propertyField;
+
+    public FieldHolder(String propertyName, Field propertyField) {
+      this.propertyName = propertyName;
+      this.propertyField = propertyField;
+    }
+
+    public static FieldHolder create(Field field) {
+      EdmProperty ep = field.getAnnotation(EdmProperty.class);
+      String name;
+      if (ep != null && !ep.name().isEmpty()) {
+        name = ep.name();
+      } else {
+        name = ANNOTATION_HELPER.getCanonicalName(field);
+      }
+      return new FieldHolder(name, field);
+    }
+
+    public void set(Object instance, Object value) {
+      set(instance, String.valueOf(value));
+    }
+
+    public void set(Object instance, String value) {
+      try {
+        boolean accessible = propertyField.isAccessible();
+        if (!accessible) {
+          propertyField.setAccessible(true);
+        }
+
+        //
+        Class<?> type = propertyField.getType();
+        if (type == Boolean.class || type == boolean.class) {
+          propertyField.set(instance, Boolean.valueOf(value));
+        } else if (type == Integer.class || type == int.class) {
+          propertyField.set(instance, Integer.valueOf(value));
+        } else if (type == Long.class || type == long.class) {
+          propertyField.set(instance, Integer.valueOf(value));
+        } else {
+          propertyField.set(instance, value);
+        }
+        //
+
+        if (!accessible) {
+          propertyField.setAccessible(false);
+        }
+      } catch (Exception ex) {
+      }
+    }
+
+    @Override
+    public String toString() {
+      return "FieldHolder{" + "propertyName=" + propertyName + ", propertyField=" + propertyField + '}';
+    }
+  }
+
+  /**
+   *
+   */
+  static final class DataSourceHolder {
+
+    private final String name;
+    private final Object dataSourceInstance;
+    private final Class<?> entityTypeClass;
+    private Method readMethod;
+    private Method createMethod;
+    private Method updateMethod;
+    private Method deleteMethod;
+    private Method setReadMethod;
+
+    public DataSourceHolder(Class<?> clz) {
+      EntityDataSource eds = clz.getAnnotation(EntityDataSource.class);
+      entityTypeClass = eds.entityType();
+      EdmEntityType entityType = entityTypeClass.getAnnotation(EdmEntityType.class);
+      if (entityType == null) {
+        throw new IllegalArgumentException("Missing EdmEntityType Annotation at class " + clz);
+      }
+
+      if (entityType.name().isEmpty()) {
+        name = ANNOTATION_HELPER.getCanonicalName(entityTypeClass);
+      } else {
+        name = entityType.name();
+      }
+      dataSourceInstance = createInstance(clz);
+      initMethods(clz);
+    }
+
+    private void initMethods(Class<?> clz) throws IllegalArgumentException, SecurityException {
+      Method[] methods = clz.getDeclaredMethods();
+      for (Method method : methods) {
+        EntityRead ec = method.getAnnotation(EntityRead.class);
+        if (ec != null) {
+          readMethod = method;
+        }
+        EntityCreate ep = method.getAnnotation(EntityCreate.class);
+        if (ep != null) {
+          createMethod = method;
+        }
+        EntityUpdate update = method.getAnnotation(EntityUpdate.class);
+        if (update != null) {
+          updateMethod = method;
+        }
+        EntityDelete delete = method.getAnnotation(EntityDelete.class);
+        if (delete != null) {
+          deleteMethod = method;
+        }
+        EntitySetRead readSet = method.getAnnotation(EntitySetRead.class);
+        if (readSet != null) {
+          setReadMethod = method;
+        }
+      }
+
+      validateMethods(clz);
+    }
+
+    private void validateMethods(Class<?> clz) throws IllegalArgumentException {
+      //
+      if (readMethod == null) {
+        throw new IllegalArgumentException("Missing " + EntityRead.class
+                + " annotation at " + EntityDataSource.class + " annotated class " + clz);
+      }
+      if (updateMethod == null) {
+        throw new IllegalArgumentException("Missing " + EntityUpdate.class
+                + " annotation at " + EntityDataSource.class + " annotated class " + clz);
+      }
+      if (createMethod == null) {
+        throw new IllegalArgumentException("Missing " + EntityCreate.class
+                + " annotation at " + EntityDataSource.class + " annotated class " + clz);
+      }
+    }
+
+    public Object readEntity(List<KeyPredicate> keys) {
+      Object[] parameterKeys = mapParameterKeys(readMethod, keys);
+      return invoke(readMethod, parameterKeys);
+    }
+
+    private Object[] mapParameterKeys(Method method, List<KeyPredicate> keys) throws IllegalStateException {
+      Class<?>[] pTypes = method.getParameterTypes();
+      if (pTypes.length != keys.size()) {
+        throw new IllegalStateException("Wrong amount of key properties. Expected read keys = "
+                + Arrays.toString(pTypes) + " given key predicates = " + keys);
+      }
+      Object[] parameterKeys = new Object[pTypes.length];
+      int i = 0;
+      for (KeyPredicate keyPredicate : keys) {
+        if (matches(pTypes[i], keyPredicate)) {
+          parameterKeys[i] = keyPredicate.getLiteral();
+        }
+        i++;
+      }
+      return parameterKeys;
+    }
+
+    public Object createEntity(Object key) {
+      return invoke(createMethod, new Object[]{key});
+    }
+
+    public Object updateEntity(Object key) {
+      return invoke(updateMethod, new Object[]{key});
+    }
+
+    public Object deleteEntity(List<KeyPredicate> keys) {
+      Object[] parameterKeys = mapParameterKeys(deleteMethod, keys);
+      return invoke(deleteMethod, parameterKeys);
+    }
+
+    public Object readEntitySet() {
+      return invoke(setReadMethod, new Object[0]);
+    }
+
+    private Object invoke(Method m, Object[] objs) {
+      try {
+        return m.invoke(dataSourceInstance, objs);
+      } catch (Exception ex) {
+        return null;
+      }
+    }
+
+    public Object createEntityInstance() {
+      return createInstance(this.entityTypeClass);
+    }
+
+    private static Object createInstance(Class<?> clz) {
+      try {
+        return clz.newInstance();
+      } catch (Exception ex) {
+        return null;
+      }
+    }
+
+    public String getEntityName() {
+      return this.name;
+    }
+
+    @Override
+    public String toString() {
+      return "DataSourceHolder{" + "name=" + name + ", dataSourceInstance=" + dataSourceInstance + 
+              ", entityTypeClass=" + entityTypeClass + ", consumerMethod=" + readMethod + 
+              ", createMethod=" + createMethod + ", updateMethod=" + updateMethod + '}';
+    }
+
+    private boolean matches(Class<?> aClass, KeyPredicate type) {
+      return true;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java
new file mode 100644
index 0000000..310e197
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * 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.annotation.processor.json;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntitySet;
+
+import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
+import org.apache.olingo.odata2.core.annotation.edm.AnnotationHelper;
+import org.apache.olingo.odata2.core.annotation.edm.ClassHelper;
+import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
+
+public class EdmAnnotationSerializer {
+
+  private final String baseUri;
+  private static final AnnotationHelper ANNOTATION_HELPER = new AnnotationHelper();
+
+  public EdmAnnotationSerializer(String baseUri) {
+    this.baseUri = baseUri;
+  }
+
+  public InputStream serialize(Object obj) {
+    byte[] buf = getContent(obj).getBytes();
+    InputStream stream = new ByteArrayInputStream(buf);
+    return stream;
+  }
+
+  private String getContent(Object entity) {
+    if (entity == null) {
+      return "NULL";
+    } else if (isConsumable(entity)) {
+      try {
+        return handleEdmAnnotations(entity);
+      } catch (Exception e) {
+        throw new ODataRuntimeException("Exception with following message occured: " + e.getMessage(), e);
+      }
+    } else {
+      return entity.toString();
+    }
+  }
+
+  private String handleEdmAnnotations(Object entity) 
+          throws IllegalArgumentException, IllegalAccessException, IOException {
+    //
+    Writer writer = new StringWriter();
+    JsonWriter json = new JsonWriter(writer);
+    json.start();
+    writeObject(entity, json);
+    json.finish();
+    writer.close();
+    //
+    return writer.toString();
+  }
+
+  private void writeObject(Object entity, JsonWriter json) throws IllegalAccessException {
+    if(entity.getClass().isArray()) {
+      List<Object> entities = Arrays.asList(entity);
+      writeCollection(entities, json);
+    } else if(Collection.class.isAssignableFrom(entity.getClass())) {
+      Collection<Object> entities = (Collection<Object>) entity;
+      writeCollection(entities, json);
+    } else {
+      writeSingleObject(entity, json);
+    }
+  }
+
+  private void writeCollection(Collection<Object> entities, JsonWriter json) throws IllegalAccessException {
+    json.startObject();
+    json.writeName("results");
+    json.startArray();
+    boolean writeSeperator = false;
+    for (Object object : entities) {
+      if(writeSeperator) {
+        json.writeSeparator();
+      } else {
+        writeSeperator = true;
+      }
+      writeSingleObject(object, json);
+    }
+    json.endArray();
+    json.endObject();
+  }
+  
+  private void writeSingleObject(Object entity, JsonWriter json) throws IllegalAccessException {
+    List<Field> fields = getAllFields(entity.getClass());
+    json.startObject();
+    for (Field field : fields) {
+      boolean written = writeEdmProperty(entity, json, field);
+      if (!written) {
+        writeEdmNavigationProperty(entity, json, field);
+      }
+    }
+    json.endObject();
+  }
+
+  private boolean writeEdmNavigationProperty(Object entity, JsonWriter json, Field field) 
+          throws IllegalArgumentException, IllegalAccessException {
+    EdmNavigationProperty navProperty = field.getAnnotation(EdmNavigationProperty.class);
+    EdmEntitySet property = entity.getClass().getAnnotation(EdmEntitySet.class);
+    if (navProperty != null) {
+      field.setAccessible(true);
+      Object keyValue = extractEdmKey(entity);
+      json.writeProperty("uri", baseUri + property.name() + "('" + keyValue.toString() + "')" 
+              + "/" + navProperty.relationship());
+      return true;
+    }
+    return false;
+  }
+
+  private boolean writeEdmProperty(Object entity, JsonWriter json, Field field) throws IllegalAccessException {
+    EdmProperty property = field.getAnnotation(EdmProperty.class);
+    if (property != null) {
+      field.setAccessible(true);
+      Object fieldValue = field.get(entity);
+      String name = getName(property, field);
+      Class<?> defaultType = getType(property);
+      json.writeProperty(name, fieldValue, defaultType);
+      return true;
+    }
+    return false;
+  }
+
+  private Class<?> getType(EdmProperty property) {
+    Class<?> defaultType = property.type().getEdmSimpleTypeInstance().getDefaultType();
+    return defaultType;
+  }
+  
+
+  private Object extractEdmKey(Object value) throws IllegalArgumentException, IllegalAccessException {
+    Field idField = getFieldWithAnnotation(value.getClass(), EdmKey.class);
+    if (idField == null) {
+      return "NULL";
+    }
+    idField.setAccessible(true);
+    return idField.get(value);
+  }
+
+  private Field getFieldWithAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClazz) {
+
+    Field[] fields = clazz.getDeclaredFields();
+    for (Field field : fields) {
+      if (field.getAnnotation(annotationClazz) != null) {
+        return field;
+      }
+    }
+
+    Class<?> superclass = clazz.getSuperclass();
+    if (superclass != Object.class) {
+      return getFieldWithAnnotation(superclass, annotationClazz);
+    }
+
+    return null;
+  }
+
+  private List<Field> getAllFields(Class<?> clazz) {
+    Field[] fields = clazz.getDeclaredFields();
+    List<Field> allFields = new ArrayList<Field>(Arrays.asList(fields));
+
+    final Class<?> superclass = clazz.getSuperclass();
+    if (superclass != Object.class) {
+      allFields.addAll(getAllFields(superclass));
+    }
+
+    return allFields;
+  }
+
+  private boolean isConsumable(Object entity) {
+    if(entity == null) {
+      return false;
+    } else if(ANNOTATION_HELPER.isEdmAnnotated(entity)) {
+      return true;
+    } else if(entity.getClass().isArray() || entity.getClass().isAssignableFrom(Collection.class)) {
+      return true;
+    } else if(Collection.class.isAssignableFrom(entity.getClass())) {
+      return true;
+    }
+    return false;
+  }
+
+  private String getName(EdmProperty property, Field field) {
+    String name = property.name();
+    if(name.isEmpty()) {
+      name = ANNOTATION_HELPER.getCanonicalName(field);
+    }
+    return name;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java
new file mode 100644
index 0000000..4a6d9ad
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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.annotation.processor.json;
+
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.olingo.odata2.core.ep.util.FormatJson;
+
+/**
+ *
+ */
+public class JsonConsumer {
+
+  private final JsonReader reader;
+  private final Map<String, String> properties;
+
+  public JsonConsumer(InputStream content) {
+    reader = new JsonReader(new InputStreamReader(content));
+    properties = new HashMap<String, String>();
+  }
+
+  public static Map<String, String> readContent(InputStream content) throws IOException {
+    JsonConsumer jc = new JsonConsumer(content);
+    return jc.read();
+  }
+
+  public Map<String, String> read() throws IOException {
+
+    reader.beginObject();
+    String nextName = reader.nextName();
+    if (FormatJson.D.equals(nextName)) {
+      reader.beginObject();
+      readEntryContent();
+      reader.endObject();
+    } else {
+      handleName(nextName);
+      readEntryContent();
+    }
+    reader.endObject();
+
+    return properties;
+  }
+
+  private void readEntryContent() throws IOException {
+    while (reader.hasNext()) {
+      final String name = reader.nextName();
+      handleName(name);
+    }
+  }
+
+  private void handleName(final String name) throws IOException {
+    if (FormatJson.METADATA.equals(name)) {
+      readMetadata();
+    } else {
+      readPropertyValue(name);
+    }
+  }
+
+  private void readPropertyValue(final String name) throws IOException {
+    final JsonToken tokenType = reader.peek();
+    final String value;
+
+    switch (tokenType) {
+      case STRING:
+        value = reader.nextString();
+        break;
+      case BOOLEAN:
+        value = String.valueOf(reader.nextBoolean());
+        break;
+      case NULL:
+        reader.nextNull();
+      default:
+        value = null;
+    }
+
+    properties.put(name, value);
+  }
+
+  private void readMetadata() throws IOException {
+    reader.beginObject();
+
+    while (reader.hasNext()) {
+      String name = reader.nextName();
+
+      if (FormatJson.PROPERTIES.equals(name)) {
+        reader.skipValue();
+        continue;
+      }
+
+      String value = reader.nextString();
+      if (FormatJson.ID.equals(name)) {
+        properties.put("md_" + name, value);
+      } else if (FormatJson.URI.equals(name)) {
+        properties.put("md_" + name, value);
+      } else if (FormatJson.TYPE.equals(name)) {
+        properties.put("md_" + name, value);
+      } else if (FormatJson.ETAG.equals(name)) {
+        properties.put("md_" + name, value);
+      } else if (FormatJson.EDIT_MEDIA.equals(name)) {
+        properties.put("md_" + name, value);
+      } else if (FormatJson.MEDIA_SRC.equals(name)) {
+        properties.put("md_" + name, value);
+      } else if (FormatJson.MEDIA_ETAG.equals(name)) {
+        properties.put("md_" + name, value);
+      } else if (FormatJson.CONTENT_TYPE.equals(name)) {
+        properties.put("md_" + name, value);
+      } else {
+        throw new IllegalStateException("Unknown metadata");
+      }
+    }
+
+    reader.endObject();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java
new file mode 100644
index 0000000..1c1cc21
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * 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.annotation.processor.json;
+
+import java.io.IOException;
+import java.io.Writer;
+import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
+
+public class JsonWriter {
+
+  private final Writer writer;
+  private boolean firstProperty = true;
+
+  public JsonWriter(Writer writer) {
+    this.writer = writer;
+  }
+
+  public void startCallback(String functionName) {
+    try {
+      writer.write(encode(functionName) + "(");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void endCallback() {
+    try {
+      writer.write(");");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void startObject() {
+    try {
+      firstProperty = true;
+      writer.write("{\n");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void endObject() {
+    try {
+      firstProperty = false;
+      writer.write("\n}");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeName(String name) {
+    try {
+      writer.write("\"" + encode(name) + "\" : ");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void startArray() {
+    try {
+      writer.write("[\n");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void endArray() {
+    try {
+      writer.write("\n]");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeSeparator() {
+    try {
+      writer.write(", ");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeString(String value) {
+    try {
+      writer.write("\"" + encode(value) + "\"");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeNull() {
+    try {
+      writer.write("null");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeNumber(int value) {
+    try {
+      writer.write(Integer.toString(value));
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeNumber(float value) {
+    try {
+      String fvalue = Float.toString(value);
+      while (fvalue.contains(".") && fvalue.endsWith("0")) {
+        fvalue = fvalue.substring(0, fvalue.length() - 1);
+      }
+      if (fvalue.endsWith(".")) {
+        fvalue = fvalue.substring(0, fvalue.length() - 1);
+      }
+      writer.write(fvalue);
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeNumber(double value) {
+    try {
+      String fvalue = Double.toString(value);
+      while (fvalue.contains(".") && fvalue.endsWith("0")) {
+        fvalue = fvalue.substring(0, fvalue.length() - 1);
+      }
+      if (fvalue.endsWith(".")) {
+        fvalue = fvalue.substring(0, fvalue.length() - 1);
+      }
+      writer.write(fvalue);
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeBoolean(boolean value) {
+    try {
+      writer.write(value ? "true" : "false");
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  public void writeProperty(String name, Object value) {
+    if(value == null) {
+      writeProperty(name, value, null);
+    } else {
+      writeProperty(name, value, value.getClass());
+    }
+  }
+  
+  
+  void writeProperty(String name, Object value, Class<?> defaultType) {
+    if (firstProperty) {
+      firstProperty = false;
+    } else {
+      writeSeparator();
+    }
+
+    // write content
+    this.writeName(name);
+    if (value == null) {
+      writeNull();
+    } else {
+      try {
+        if(defaultType == null) {
+          writeValue(value);
+        } else {
+          writeValueAs(value, defaultType);
+        }
+      } catch (Exception e) {
+        writeString("exception for property with name '" + name
+                + "' with message '" + e.getMessage()
+                + "' and toString '" + value.toString() + "'.");
+      }
+    }
+  }
+
+  
+  public void writeRaw(String value) {
+    try {
+      writer.write(value);
+    } catch (IOException e) {
+      throw new ODataRuntimeException(e.getMessage(), e);
+    }
+  }
+
+  private String encode(String unencoded) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < unencoded.length(); i++) {
+      char c = unencoded.charAt(i);
+      if (c == '\\') {
+        sb.append("\\\\");
+      } else if (c == '"') {
+        sb.append("\\\"");
+      } else if (c == '\n') {
+        sb.append("\\n");
+      } else if (c == '\r') {
+        sb.append("\\r");
+      } else if (c == '\f') {
+        sb.append("\\f");
+      } else if (c == '\b') {
+        sb.append("\\b");
+      } else if (c == '\t') {
+        sb.append("\\t");
+      } else {
+        sb.append(c);
+      }
+    }
+    return sb.toString();
+  }
+
+  void start() {
+    writeRaw("{\n\"d\":");
+  }
+
+  void finish() {
+    writeRaw("\n}");
+  }
+
+  private void writeValueAs(Object value, Class<?> clazz) {
+    if(clazz.isAssignableFrom(String.class)) {
+      writeString(String.valueOf(value));
+    } else if(clazz.isAssignableFrom(Float.class)) {
+      writeNumber((Float) value);
+    } else if(clazz.isAssignableFrom(Double.class)) {
+      writeNumber((Double) value);
+    } else if(clazz.isAssignableFrom(Integer.class)) {
+      writeNumber((Integer) value);
+    } else {
+      writeString(value.toString());
+    }
+  }
+  
+  private void writeValue(Object value) {
+    writeValueAs(value, value.getClass());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/main/resources/log4j.xml
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/resources/log4j.xml b/odata2-edm-annotation/edm-annotation-core/src/main/resources/log4j.xml
new file mode 100644
index 0000000..90a4706
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/resources/log4j.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+    <appender name="console" class="org.apache.log4j.ConsoleAppender">
+        <param name="Target" value="System.out" />
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%-5p %c{1} - %m%n" />
+        </layout>
+    </appender>
+
+    <logger name="org.apache.olingo.odata2.ref" additivity="false">
+        <level value="trace" />
+        <appender-ref ref="console" />
+    </logger>
+    
+    <root>
+        <priority value="error" />
+        <appender-ref ref="console" />
+    </root>
+</log4j:configuration>

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java
new file mode 100644
index 0000000..509303c
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2013 The Apache Software Foundation.
+ *
+ * Licensed 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.annotation.edm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+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.AssociationSet;
+import org.apache.olingo.odata2.api.edm.provider.ComplexType;
+import org.apache.olingo.odata2.api.edm.provider.EntityContainer;
+import org.apache.olingo.odata2.api.edm.provider.EntityContainerInfo;
+import org.apache.olingo.odata2.api.edm.provider.EntitySet;
+import org.apache.olingo.odata2.api.edm.provider.EntityType;
+import org.apache.olingo.odata2.api.edm.provider.FunctionImport;
+import org.apache.olingo.odata2.api.edm.provider.NavigationProperty;
+import org.apache.olingo.odata2.api.edm.provider.Property;
+import org.apache.olingo.odata2.api.edm.provider.PropertyRef;
+import org.apache.olingo.odata2.api.edm.provider.Schema;
+import org.apache.olingo.odata2.core.annotation.model.Building;
+import org.apache.olingo.odata2.core.annotation.model.City;
+import org.apache.olingo.odata2.core.annotation.model.Employee;
+import org.apache.olingo.odata2.core.annotation.model.Location;
+import org.apache.olingo.odata2.core.annotation.model.Manager;
+import org.apache.olingo.odata2.core.annotation.model.ModelSharedConstants;
+import org.apache.olingo.odata2.core.annotation.model.Photo;
+import org.apache.olingo.odata2.core.annotation.model.RefBase;
+import org.apache.olingo.odata2.core.annotation.model.Room;
+import org.apache.olingo.odata2.core.annotation.model.Team;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+/**
+ *
+ * @author d046871
+ */
+public class AnnotationEdmProviderTest {
+
+  private final Collection<Class<?>> annotatedClasses = new ArrayList<Class<?>>();
+  private final AnnotationEdmProvider aep;
+
+  public AnnotationEdmProviderTest() {
+    annotatedClasses.add(RefBase.class);
+    annotatedClasses.add(Building.class);
+    annotatedClasses.add(City.class);
+    annotatedClasses.add(Employee.class);
+    annotatedClasses.add(Location.class);
+    annotatedClasses.add(Manager.class);
+    annotatedClasses.add(Photo.class);
+    annotatedClasses.add(Room.class);
+    annotatedClasses.add(Team.class);
+
+    aep = new AnnotationEdmProvider(annotatedClasses);
+  }
+
+  @Test
+  public void loadAnnotatedClassesFromPackage() throws Exception {
+    AnnotationEdmProvider localAep = new AnnotationEdmProvider("org.apache.olingo.odata2.core.annotation.model");
+
+    // validate employee
+    EntityType employee = localAep.getEntityType(new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "Employee"));
+    assertEquals("Employee", employee.getName());
+    final List<PropertyRef> employeeKeys = employee.getKey().getKeys();
+    assertEquals(1, employeeKeys.size());
+    assertEquals("EmployeeId", employeeKeys.get(0).getName());
+    assertEquals(4, employee.getProperties().size());
+    assertEquals(1, employee.getNavigationProperties().size());
+
+    List<Schema> schemas = localAep.getSchemas();
+    assertEquals(1, schemas.size());
+    EntityContainerInfo info = localAep.getEntityContainerInfo(ModelSharedConstants.CONTAINER_1);
+    assertTrue(info.isDefaultEntityContainer());
+  }
+
+  @Test
+  public void annotationProviderBasic() throws Exception {
+    assertNotNull(aep);
+
+    List<Schema> schemas = aep.getSchemas();
+    assertEquals(1, schemas.size());
+    EntityContainerInfo info = aep.getEntityContainerInfo(ModelSharedConstants.CONTAINER_1);
+    assertTrue(info.isDefaultEntityContainer());
+    
+    FunctionImport funImp = aep.getFunctionImport(ModelSharedConstants.CONTAINER_1, "NoImport");
+    assertNull(funImp);
+
+    final FullQualifiedName associationFqn = new FullQualifiedName(
+            ModelSharedConstants.NAMESPACE_1, "NoAssociation");
+    Association noAssociation = aep.getAssociation(associationFqn);
+    assertNull(noAssociation);
+    
+    AssociationSet noAssociationSet = aep.getAssociationSet(
+            ModelSharedConstants.CONTAINER_1, associationFqn, "NoSrc", "NoSrcEntity");
+    assertNull(noAssociationSet);
+    
+    AssociationSet asBuildingRooms = aep.getAssociationSet(
+            ModelSharedConstants.CONTAINER_1, defaultFqn("BuildingRooms"), "Buildings", "r_Building");
+    assertNotNull(asBuildingRooms);
+    assertEquals("Buildings", asBuildingRooms.getEnd1().getEntitySet());
+    assertEquals("r_Building", asBuildingRooms.getEnd1().getRole());
+    assertEquals("Rooms", asBuildingRooms.getEnd2().getEntitySet());
+    assertEquals("r_Room", asBuildingRooms.getEnd2().getRole());
+  }
+
+  @Test
+  public void annotationProviderGetDefaultContainer() throws Exception {
+    assertNotNull(aep);
+
+    List<Schema> schemas = aep.getSchemas();
+    assertEquals(1, schemas.size());
+    EntityContainerInfo info = aep.getEntityContainerInfo(null);
+    assertTrue(info.isDefaultEntityContainer());
+    assertEquals(ModelSharedConstants.CONTAINER_1, info.getName());
+  }
+
+  @Test
+  public void schemaBasic() throws Exception {
+    assertNotNull(aep);
+
+    List<Schema> schemas = aep.getSchemas();
+    assertEquals(1, schemas.size());
+
+    Schema schema = schemas.get(0);
+    List<EntityContainer> containers = schema.getEntityContainers();
+    assertEquals(1, containers.size());
+    EntityContainer container = containers.get(0);
+    assertEquals(ModelSharedConstants.CONTAINER_1, container.getName());
+    final List<EntitySet> entitySets = container.getEntitySets();
+    assertEquals(5, entitySets.size());
+    
+    List<Association> associations = schema.getAssociations();
+    assertEquals(4, associations.size());
+    for (Association association : associations) {
+      assertNotNull(association.getName());
+      validateAssociation(association);
+    }
+  }
+  
+  private FullQualifiedName defaultFqn(String name) {
+    return new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, name);
+  }
+
+  private void validateAssociation(Association association) {
+    String name = association.getName();
+    if(name.equals("RoomEmployees")) {
+      validateAssociation(association, 
+              "r_Room", EdmMultiplicity.ONE, defaultFqn("Room"),
+              "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"));
+    } else if(name.equals("ManagerEmployees")) {
+        validateAssociation(association, 
+                "r_Manager", EdmMultiplicity.ONE, defaultFqn("Manager"),
+                "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
+    } else if(name.equals("TeamEmployees")) {
+        validateAssociation(association, 
+                "r_Team", EdmMultiplicity.ONE, defaultFqn("Team"),
+                "r_Employees", EdmMultiplicity.MANY, defaultFqn("Employee"));
+    } else {
+        fail("Got unknown association to validate with name '" + name + "'.");
+    }
+  }
+
+  private void validateAssociation(Association association, 
+          String fromRole, EdmMultiplicity fromMulti, FullQualifiedName fromType, 
+          String toRole, EdmMultiplicity toMulti, FullQualifiedName toType) {
+
+    AssociationEnd[] ends = new AssociationEnd[]{association.getEnd1(),association.getEnd2()};
+    for (AssociationEnd associationEnd : ends) {
+      if(associationEnd.getRole().equals(fromRole)) {
+        validateAssociationEnd(associationEnd, fromRole, fromMulti, fromType);
+      } else if(associationEnd.getRole().equals(toRole)) {
+        validateAssociationEnd(associationEnd, toRole, toMulti, toType);
+      } else {
+        fail("Unexpected navigation end '" + associationEnd.getRole() 
+                + "' for association with name '" + association.getName() + "'.");
+      }
+    }
+  }
+
+    private void validateAssociationEnd(AssociationEnd associationEnd, 
+          String role, EdmMultiplicity multiplicity, FullQualifiedName type) {
+    assertEquals(role, associationEnd.getRole());
+    assertEquals(multiplicity, associationEnd.getMultiplicity());
+    assertEquals(type, associationEnd.getType());
+  }
+
+
+  @Test
+  public void entitySetTeams() throws Exception {
+    // validate teams
+    EntitySet teams = aep.getEntitySet(ModelSharedConstants.CONTAINER_1, "Teams");
+    assertEquals(ModelSharedConstants.NAMESPACE_1, teams.getEntityType().getNamespace());
+    assertEquals("Team", teams.getEntityType().getName());
+  }
+
+  @Test
+  public void entityTypeEmployee() throws Exception {
+    // validate employee
+    EntityType employee = aep.getEntityType(new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "Employee"));
+    assertEquals("Employee", employee.getName());
+    final List<PropertyRef> employeeKeys = employee.getKey().getKeys();
+    assertEquals(1, employeeKeys.size());
+    assertEquals("EmployeeId", employeeKeys.get(0).getName());
+    assertEquals(4, employee.getProperties().size());
+    assertEquals(1, employee.getNavigationProperties().size());
+  }
+
+  @Test
+  public void entityTypeTeam() throws Exception {
+    // validate team
+    EntityType team = aep.getEntityType(new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "Team"));
+    assertEquals("Team", team.getName());
+    assertEquals("Base", team.getBaseType().getName());
+    assertEquals(ModelSharedConstants.NAMESPACE_1, team.getBaseType().getNamespace());
+
+    assertEquals(1, team.getProperties().size());
+    assertEquals(1, team.getNavigationProperties().size());
+  }
+
+  @Test
+  public void entityTypeAbstractBaseType() throws Exception {
+    // validate employee
+    EntityType baseType = aep.getEntityType(new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "Base"));
+    assertEquals("Base", baseType.getName());
+    final List<PropertyRef> keys = baseType.getKey().getKeys();
+    assertEquals(1, keys.size());
+    assertEquals("Id", keys.get(0).getName());
+    assertEquals(2, baseType.getProperties().size());
+    assertTrue(baseType.isAbstract());
+
+    // validate base for team
+    EntityType team = aep.getEntityType(new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "Team"));
+    assertEquals("Team", team.getName());
+    assertEquals("Base", team.getBaseType().getName());
+    assertEquals(ModelSharedConstants.NAMESPACE_1, team.getBaseType().getNamespace());
+  }
+
+  @Test
+  public void complexTypeLocation() throws Exception {
+    // validate employee
+    EntityType employee = aep.getEntityType(new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "Employee"));
+    final List<Property> properties = employee.getProperties();
+    Property location = null;
+    for (Property property : properties) {
+      if (property.getName().equals("Location")) {
+        location = property;
+      }
+    }
+    assertNotNull(location);
+    assertEquals("Location", location.getName());
+
+    // validate location complex type
+    ComplexType locationType = aep.getComplexType(
+            new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "c_Location"));
+    assertEquals("c_Location", locationType.getName());
+    assertEquals(2, locationType.getProperties().size());
+  }
+
+  @Test
+  public void entityTypeRoomWithNavigation() throws Exception {
+    // validate employee
+    EntityType room = aep.getEntityType(new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, "Room"));
+    assertEquals("Room", room.getName());
+    assertEquals("Base", room.getBaseType().getName());
+    assertEquals(2, room.getProperties().size());
+    final List<NavigationProperty> navigationProperties = room.getNavigationProperties();
+    assertEquals(2, navigationProperties.size());
+    
+    for (NavigationProperty navigationProperty : navigationProperties) {
+      if(navigationProperty.getName().equals("nr_Employees")) {
+        validateNavProperty(navigationProperty, "RoomEmployees", "r_Room", "r_Employees");
+      } else if(navigationProperty.getName().equals("nr_Building")) {
+        validateNavProperty(navigationProperty, "BuildingRooms", "r_Room", "r_Building");
+      } else {
+        fail("Got unexpected navigation property with name '" + navigationProperty.getName() + "'.");
+      }
+    }
+  }
+  
+  private void validateNavProperty(NavigationProperty navigationProperty, String name,
+          String relationship, String fromRole, String toRole) {
+    if(name != null) {
+      assertEquals(name, navigationProperty.getName());
+    }
+    FullQualifiedName fqn = new FullQualifiedName(ModelSharedConstants.NAMESPACE_1, relationship);
+    assertEquals("Wrong relationship for navigation property.", fqn, navigationProperty.getRelationship());
+    assertEquals("Wrong fromRole for navigation property.", fromRole, navigationProperty.getFromRole());
+    assertEquals("Wrong toRole for navigation property.", toRole, navigationProperty.getToRole());
+  }
+  
+  private void validateNavProperty(NavigationProperty navigationProperty, 
+          String relationship, String fromRole, String toRole) {
+    validateNavProperty(navigationProperty, null, relationship, fromRole, toRole);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java
new file mode 100644
index 0000000..1da4bdb
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
+import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+import org.apache.olingo.odata2.api.annotation.edm.NavigationEnd;
+import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
+import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
+
+/**
+ *  
+ */
+@EdmEntityType(name="Building", namespace=ModelSharedConstants.NAMESPACE_1)
+@EdmEntitySet(name="Buildings", container="Container1")
+public class Building {
+  @EdmKey
+  @EdmProperty(type = EdmSimpleTypeKind.String)
+  private int id;
+  @EdmProperty
+  private String name;
+  private byte[] image;
+  @EdmNavigationProperty(name="nb_rooms", relationship="BuildingRooms",
+          to = @NavigationEnd(role = "r_Room", type="Room", multiplicity = EdmMultiplicity.MANY))
+  private List<Room> rooms = new ArrayList<Room>();
+
+  public Building() {
+  }
+
+  public Building(final int id, final String name) {
+    this.id = id;
+    setName(name);
+  }
+
+  public String getId() {
+    return Integer.toString(id);
+  }
+
+  public void setName(final String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setImage(final byte[] byteArray) {
+    image = byteArray;
+  }
+
+  public byte[] getImage() {
+    if (image == null) {
+      return null;
+    } else {
+      return image.clone();
+    }
+  }
+
+  public List<Room> getRooms() {
+    return rooms;
+  }
+
+  @Override
+  public int hashCode() {
+    return id;
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    return this == obj
+        || obj != null && getClass() == obj.getClass() && id == ((Building) obj).id;
+  }
+
+  @Override
+  public String toString() {
+    return "{\"Id\":\"" + id + "\",\"Name\":\"" + name + "\",\"Image\":\"" + Arrays.toString(image) + "\"}";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java
new file mode 100644
index 0000000..6a117db
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+import org.apache.olingo.odata2.api.annotation.edm.EdmComplexEntity;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+
+/**
+ *  
+ */
+@EdmComplexEntity(name="c_City", namespace = ModelSharedConstants.NAMESPACE_1)
+public class City {
+
+  @EdmProperty
+  private String postalCode;
+  @EdmProperty
+  private String cityName;
+
+  public City(final String postalCode, final String name) {
+    this.postalCode = postalCode;
+    cityName = name;
+  }
+
+  public void setPostalCode(final String postalCode) {
+    this.postalCode = postalCode;
+  }
+
+  public String getPostalCode() {
+    return postalCode;
+  }
+
+  public void setCityName(final String cityName) {
+    this.cityName = cityName;
+  }
+
+  public String getCityName() {
+    return cityName;
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%s, %s", cityName, postalCode);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java
new file mode 100644
index 0000000..0323ecc
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+import org.apache.olingo.odata2.api.annotation.edm.EdmComplexProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
+import org.apache.olingo.odata2.api.annotation.edm.NavigationEnd;
+import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
+import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
+
+/**
+ *  
+ */
+@EdmEntityType(name="Employee", namespace=ModelSharedConstants.NAMESPACE_1)
+@EdmEntitySet(name="Employees", container=ModelSharedConstants.CONTAINER_1)
+public class Employee {
+  private static int counter = 1;
+  @EdmProperty(name="EmployeeId", type = EdmSimpleTypeKind.Int32)
+  @EdmKey
+  private int employeeId;
+  @EdmProperty(name="EmployeeName")
+  private String employeeName;
+  @EdmProperty
+  private int age;
+  @EdmNavigationProperty(name="ne_Manager", relationship="ManagerEmployees",
+          from = @NavigationEnd(role="r_Employees", multiplicity = EdmMultiplicity.MANY))//,
+//          to = @NavigationEnd(type = "Manager"))
+  private Manager manager;
+  private Team team;
+  private Room room;
+  private String imageType;
+  private byte[] image;
+  private String imageUrl;
+  private Calendar entryDate;
+  @EdmComplexProperty(name="Location")
+  private Location location;
+
+  public Employee(final int employeeId, final String name) {
+    this.employeeId = employeeId;
+    setEmployeeName(name);
+  }
+
+  public String getId() {
+    return Integer.toString(employeeId);
+  }
+
+  public void setEmployeeName(final String employeeName) {
+    this.employeeName = employeeName;
+  }
+
+  public String getEmployeeName() {
+    return employeeName;
+  }
+
+  public void setAge(final int age) {
+    this.age = age;
+  }
+
+  public int getAge() {
+    return age;
+  }
+
+  public void setManager(final Manager manager) {
+    this.manager = manager;
+  }
+
+  public Manager getManager() {
+    return manager;
+  }
+
+  public void setTeam(final Team team) {
+    this.team = team;
+  }
+
+  public Team getTeam() {
+    return team;
+  }
+
+  public void setRoom(final Room room) {
+    this.room = room;
+  }
+
+  public Room getRoom() {
+    return room;
+  }
+
+  public void setImageUri(final String imageUri) {
+    imageUrl = imageUri;
+  }
+
+  public String getImageUri() {
+    return imageUrl;
+  }
+
+  public void setLocation(final Location location) {
+    this.location = location;
+  }
+
+  public Location getLocation() {
+    return location;
+  }
+
+  public void setEntryDate(final Calendar date) {
+    entryDate = date;
+  }
+
+  public Calendar getEntryDate() {
+    return entryDate;
+  }
+
+  public void setImageType(final String imageType) {
+    this.imageType = imageType;
+  }
+
+  public String getImageType() {
+    return imageType;
+  }
+
+  public void setImage(final byte[] image) {
+    this.image = image;
+  }
+
+  public byte[] getImage() {
+    if (image == null) {
+      return null;
+    }
+    return image.clone();
+  }
+
+  @Override
+  public int hashCode() {
+    return employeeId;
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    return this == obj
+        || obj != null && getClass() == obj.getClass() && employeeId == ((Employee) obj).employeeId;
+  }
+
+  @Override
+  public String toString() {
+    return "{\"EmployeeId\":\"" + employeeId + "\","
+        + "\"EmployeeName\":\"" + employeeName + "\","
+        + "\"ManagerId\":" + (manager == null ? "null" : "\"" + manager.getId() + "\"") + ","
+        + "\"RoomId\":" + (room == null ? "null" : "\"" + room.getId() + "\"") + ","
+        + "\"TeamId\":" + (team == null ? "null" : "\"" + team.getId() + "\"") + ","
+        + "\"Location\":"
+        + (location == null ? "null" :
+            "{\"City\":" + (location.getCity() == null ? "null" :
+                "{\"PostalCode\":\"" + location.getCity().getPostalCode() + "\","
+                    + "\"CityName\":\"" + location.getCity().getCityName() + "\"}") + ","
+                + "\"Country\":\"" + location.getCountry() + "\"}") + ","
+        + "\"Age\":" + age + ","
+        + "\"EntryDate\":"
+        + (entryDate == null ? "null" : "\"" + DateFormat.getInstance().format(entryDate.getTime()) + "\"") + ","
+        + "\"ImageUrl\":\"" + imageUrl + "\"}";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java
new file mode 100644
index 0000000..3b74c30
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+import org.apache.olingo.odata2.api.annotation.edm.EdmComplexEntity;
+import org.apache.olingo.odata2.api.annotation.edm.EdmComplexProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+
+/**
+ *  
+ */
+@EdmComplexEntity(name = "c_Location", namespace = ModelSharedConstants.NAMESPACE_1)
+public class Location {
+  @EdmProperty
+  private String country;
+  @EdmComplexProperty
+  private City city;
+
+  public Location(final String country, final String postalCode, final String cityName) {
+    this.country = country;
+    city = new City(postalCode, cityName);
+  }
+
+  public void setCountry(final String country) {
+    this.country = country;
+  }
+
+  public String getCountry() {
+    return country;
+  }
+
+  public void setCity(final City city) {
+    this.city = city;
+  }
+
+  public City getCity() {
+    return city;
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%s, %s", country, city.toString());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java
new file mode 100644
index 0000000..1408c9c
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
+import org.apache.olingo.odata2.api.annotation.edm.NavigationEnd;
+
+/**
+ *
+ */
+@EdmEntityType(name = "Manager", namespace = ModelSharedConstants.NAMESPACE_1)
+@EdmEntitySet(name = "Managers", container = ModelSharedConstants.CONTAINER_1)
+public class Manager extends Employee {
+
+  @EdmNavigationProperty(name = "nm_Employees", relationship = "ManagerEmployees",
+          to = @NavigationEnd(role = "r_Employees", type = "Employee"))
+  private List<Employee> employees = new ArrayList<Employee>();
+
+  public Manager(final int id, final String name) {
+    super(id, name);
+  }
+
+  public List<Employee> getEmployees() {
+    return employees;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/ModelSharedConstants.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/ModelSharedConstants.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/ModelSharedConstants.java
new file mode 100644
index 0000000..f87165f
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/ModelSharedConstants.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+public interface ModelSharedConstants {
+
+  String NAMESPACE_1 = "RefScenario";
+  String CONTAINER_1 = "Container1";
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Photo.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Photo.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Photo.java
new file mode 100644
index 0000000..6c89654
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Photo.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+import java.util.Arrays;
+
+/**
+ *  
+ */
+public class Photo {
+  private static final String RESOURCE = "/male_1_WinterW.jpg";
+
+  private final int id;
+  private String name;
+  private String type = "image/jpeg";
+  private String imageUrl = "http://localhost" + RESOURCE;
+  private byte[] image = new byte[0];
+  private String imageType = type;
+  private byte[] binaryData;
+  private String content;
+
+  public Photo(final int id, final String name, final String type) {
+    this.id = id;
+    setName(name);
+    setType(type);
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(final String name) {
+    this.name = name;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(final String type) {
+    this.type = type;
+  }
+
+  public String getImageUri() {
+    return imageUrl;
+  }
+
+  public void setImageUri(final String uri) {
+    imageUrl = uri;
+  }
+
+  public byte[] getImage() {
+    return image.clone();
+  }
+
+  public void setImage(final byte[] image) {
+    this.image = image;
+  }
+
+  public String getImageType() {
+    return imageType;
+  }
+
+  public void setImageType(final String imageType) {
+    this.imageType = imageType;
+  }
+
+  public byte[] getBinaryData() {
+    if (binaryData == null) {
+      return null;
+    } else {
+      return binaryData.clone();
+    }
+  }
+
+  public void setBinaryData(final byte[] binaryData) {
+    this.binaryData = binaryData;
+  }
+
+  public void setContent(final String content) {
+    this.content = content;
+  }
+
+  public String getContent() {
+    return content;
+  }
+
+  @Override
+  public int hashCode() {
+    return id;
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    return this == obj
+        || obj != null && getClass() == obj.getClass() && id == ((Photo) obj).id;
+  }
+
+  @Override
+  public String toString() {
+    return "{\"Id\":" + id + ","
+        + "\"Name\":\"" + name + "\","
+        + "\"Type\":\"" + type + "\","
+        + "\"ImageUrl\":\"" + imageUrl + "\","
+        + "\"Image\":\"" + Arrays.toString(image) + "\","
+        + "\"ImageType\":\"" + imageType + "\","
+        + "\"Content:\"" + content + "\","
+        + "\"BinaryData\":\"" + Arrays.toString(binaryData) + "\"}";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java
new file mode 100644
index 0000000..15a7dcc
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 The Apache Software Foundation.
+ *
+ * Licensed 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.annotation.model;
+
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
+import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
+
+/**
+ *
+ */
+@EdmEntityType(name="Base", namespace=ModelSharedConstants.NAMESPACE_1)
+public abstract class RefBase {
+  @EdmProperty(name="Name")
+  protected String name;
+  @EdmProperty(name="Id", type = EdmSimpleTypeKind.String)
+  @EdmKey
+  protected int id;
+
+  public RefBase(int id, String name) {
+    this.name = name;
+    this.id = id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getId() {
+    return Integer.toString(id);
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java
new file mode 100644
index 0000000..8c8a62d
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013 The Apache Software Foundation.
+ *
+ * Licensed 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.annotation.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+import org.apache.olingo.odata2.api.annotation.edm.NavigationEnd;
+import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
+
+/**
+ *
+ */
+@EdmEntityType(name = "Room", namespace = ModelSharedConstants.NAMESPACE_1)
+@EdmEntitySet(name = "Rooms", container = ModelSharedConstants.CONTAINER_1)
+public class Room extends RefBase {
+
+  @EdmProperty
+  private Integer seats;
+  @EdmProperty
+  private Integer version;
+  @EdmNavigationProperty(name = "nr_Building", relationship = "BuildingRooms",
+          from = @NavigationEnd(role = "r_Room", multiplicity = EdmMultiplicity.MANY))
+  private Building building;
+  @EdmNavigationProperty(name = "nr_Employees", relationship = "RoomEmployees",
+          from = @NavigationEnd(role = "r_Room", type = "Room", multiplicity = EdmMultiplicity.ONE),
+          to = @NavigationEnd(role = "r_Employees", type = "Employee", multiplicity = EdmMultiplicity.MANY))
+  private List<Employee> employees = new ArrayList<Employee>();
+
+  public Room(final int id, final String name) {
+    super(id, name);
+  }
+
+  public void setSeats(final int seats) {
+    this.seats = seats;
+  }
+
+  public int getSeats() {
+    return seats;
+  }
+
+  public void setVersion(final int version) {
+    this.version = version;
+  }
+
+  public int getVersion() {
+    return version;
+  }
+
+  public void setBuilding(final Building building) {
+    this.building = building;
+  }
+
+  public Building getBuilding() {
+    return building;
+  }
+
+  public List<Employee> getEmployees() {
+    return employees;
+  }
+
+  @Override
+  public int hashCode() {
+    return id;
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    return this == obj
+            || obj != null && getClass() == obj.getClass() && id == ((Room) obj).id;
+  }
+
+  @Override
+  public String toString() {
+    return "{\"Id\":\"" + id + "\",\"Name\":\"" + name + "\",\"Seats\":" + seats + ",\"Version\":" + version + "}";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ae7c1f52/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java
new file mode 100644
index 0000000..363ad7a
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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.annotation.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+import org.apache.olingo.odata2.api.annotation.edm.NavigationEnd;
+import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
+import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
+
+/**
+*  
+*/
+@EdmEntityType(name="Team", namespace = ModelSharedConstants.NAMESPACE_1)
+@EdmEntitySet(name="Teams", container = ModelSharedConstants.CONTAINER_1)
+public class Team extends RefBase {
+  @EdmProperty(type = EdmSimpleTypeKind.Boolean)
+  private Boolean isScrumTeam;
+  @EdmNavigationProperty(name = "ne_Teams", relationship="TeamEmployees", 
+          from = @NavigationEnd(role = "r_Team",  type = "Team"),
+          to = @NavigationEnd(role = "r_Employees", type = "Employee", multiplicity = EdmMultiplicity.MANY))
+  private List<Employee> employees = new ArrayList<Employee>();
+
+  public Team() {
+    super(-1, null);
+  }
+  
+  public Team(final int id, final String name) {
+    super(id, name);
+  }
+
+  public Boolean isScrumTeam() {
+    return isScrumTeam;
+  }
+
+  public void setScrumTeam(final Boolean isScrumTeam) {
+    this.isScrumTeam = isScrumTeam;
+  }
+  
+  public void addEmployee(Employee e) {
+    this.employees.add(e);
+  }
+
+  public List<Employee> getEmployees() {
+    return employees;
+  }
+
+  @Override
+  public int hashCode() {
+    return id;
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    return this == obj
+        || obj != null && getClass() == obj.getClass() && id == ((Team) obj).id;
+  }
+
+  @Override
+  public String toString() {
+    return "{\"Id\":\"" + id + "\",\"Name\":\"" + name + "\",\"IsScrumTeam\":" + isScrumTeam + "}";
+  }
+}