12 | * This annotation can not be parameterized, all values like name are defined
13 | * via the {@link EdmProperty} annotation. In addition the EdmKey annotation has
14 | * to be used in conjunction with an EdmProperty annotation on a field within a
15 | * EdmEntityType annotated class.
16 | *
17 | */
18 | @Retention(RetentionPolicy.RUNTIME)
19 | @Target(ElementType.FIELD)
20 | public @interface EdmKey {
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/edm/meta/EntityMetaPropertyComplex.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.edm.meta;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 |
6 | /**
7 | * This class contains the meta data of a complex property for the OData
8 | * service.
9 | */
10 | @Getter
11 | public class EntityMetaPropertyComplex extends EntityMetaProperty {
12 |
13 | /**
14 | * The type of the property value.
15 | */
16 | private final EntityMetaData> valueMetaData;
17 |
18 | @Builder
19 | private EntityMetaPropertyComplex(
20 | String name,
21 | String fieldName,
22 | Class> fieldType,
23 | Boolean nullable,
24 | EntityMetaData> valueMetaData) {
25 |
26 | super(false, name, fieldName, fieldType, nullable);
27 | this.valueMetaData = valueMetaData;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/edm/meta/EntityMetaPropertyEntity.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.edm.meta;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 |
6 | /**
7 | * This class contains the meta data of a entity (e. g. navigation) property for
8 | * the OData service.
9 | */
10 | @Getter
11 | public class EntityMetaPropertyEntity extends EntityMetaProperty {
12 |
13 | /**
14 | * The type of the property value.
15 | */
16 | private final EntityMetaData> valueMetaData;
17 |
18 | @Builder
19 | private EntityMetaPropertyEntity(
20 | String name,
21 | String fieldName,
22 | Class> fieldType,
23 | Boolean nullable,
24 | EntityMetaData> valueMetaData) {
25 |
26 | super(false, name, fieldName, fieldType, nullable);
27 | this.valueMetaData = valueMetaData;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/model/Author.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.model;
2 |
3 | import com.bloggingit.odata.edm.annotation.EdmEntitySet;
4 | import com.bloggingit.odata.edm.annotation.EdmEntityType;
5 | import com.bloggingit.odata.edm.annotation.EdmFacets;
6 | import com.bloggingit.odata.edm.annotation.EdmProperty;
7 | import lombok.AllArgsConstructor;
8 | import lombok.Builder;
9 | import lombok.Getter;
10 | import lombok.NoArgsConstructor;
11 | import lombok.Setter;
12 |
13 | @Getter
14 | @Setter
15 | @NoArgsConstructor
16 | @AllArgsConstructor
17 | @Builder
18 |
19 | @EdmEntityType
20 | @EdmEntitySet
21 | public class Author extends BaseEntity {
22 |
23 | @EdmProperty(facets = @EdmFacets(nullable = false))
24 | private String name;
25 |
26 | @EdmProperty(facets = @EdmFacets(nullable = false))
27 | private Gender gender;
28 |
29 | @EdmProperty(facets = @EdmFacets(nullable = false))
30 | private ContactInfo contactInfo;
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/edm/annotation/EdmComplexType.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.edm.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | *
10 | * Annotation for a EDM/CSDL ComplexType element.
11 | *
12 | * EdmComplexType holds a set of related information like EdmPrimitiveType
13 | * properties and EdmComplexType properties.
14 | */
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Target(ElementType.TYPE)
17 | public @interface EdmComplexType {
18 |
19 | /**
20 | * Define the name for the ComplexType. If not set a default value has to be
21 | * generated by the EDM provider.
22 | *
23 | * @return name for the ComplexType
24 | */
25 | String name() default "";
26 |
27 | /**
28 | * Define the namespace for the ComplexType. If not set a default value has
29 | * to be generated by the EDM provider.
30 | *
31 | * @return namespace for the ComplexType
32 | */
33 | String namespace() default "";
34 | }
35 |
--------------------------------------------------------------------------------
/nb-configuration.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
16 | ide
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/edm/annotation/EdmEntityType.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.edm.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Annotation for a EDM/CSDL EntityType element.
10 | *
11 | * EdmEntityType holds a set of related information like EdmPrimitiveType,
12 | * EdmComplexType and EdmNavigation properties.
13 | *
14 | */
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Target(ElementType.TYPE)
17 | public @interface EdmEntityType {
18 |
19 | /**
20 | * Define the name for the EDM EntityType. If not set a default value has to
21 | * be generated by the EDM provider.
22 | *
23 | * @return name for the EDM EntityType
24 | */
25 | String name() default "";
26 |
27 | /**
28 | * Define the namespace for the EDM EntityType. If not set a default value
29 | * has to be generated by the EDM provider.
30 | *
31 | * @return namespace for the EDM EntityType
32 | */
33 | String namespace() default "";
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/edm/annotation/EdmProperty.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.edm.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Annotation for definition of an EdmProperty for an EdmEntityType or
10 | * EdmComplexType which contains the EdmProperty as a field.
11 | *
12 | * The EdmProperty annotation has to be used on a field within a EdmEntityType
13 | * or EdmComplexType annotated class.
14 | *
15 | */
16 | @Retention(RetentionPolicy.RUNTIME)
17 | @Target(ElementType.FIELD)
18 | public @interface EdmProperty {
19 |
20 | /**
21 | * Define the name for the Property. If not set a default value has to be
22 | * generated by the EDM provider.
23 | *
24 | * @return name for the Property
25 | */
26 | String name() default "";
27 |
28 | /**
29 | * Define the EdmFacets for the Property in the EDM. If not set the default
30 | * EdmFacet values are used (see {@link EdmFacets}).
31 | *
32 | * @return facets for the Property as used in the EDM
33 | */
34 | EdmFacets facets() default @EdmFacets;
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/edm/meta/EntityMetaProperty.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.edm.meta;
2 |
3 | import com.bloggingit.odata.olingo.v4.util.DefaultValue;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Getter;
6 |
7 | /**
8 | * This abstract class is for the meta data of an entity property
9 | */
10 | @Getter
11 | @AllArgsConstructor
12 | public abstract class EntityMetaProperty {
13 |
14 | /**
15 | * true if property is annotated with @EdmKey or false if not
16 | */
17 | private final boolean isKey;
18 |
19 | /**
20 | * The name of the edm property.
21 | */
22 | private final String edmName;
23 |
24 | /**
25 | * The property name of the entity.
26 | */
27 | private final String fieldName;
28 |
29 | /**
30 | * The property type of the entity.
31 | */
32 | private final Class> fieldType;
33 |
34 | /**
35 | * true if nullable or null if not specified
36 | */
37 | private final Boolean nullable;
38 |
39 | /**
40 | * @return the default value as a String or null if not specified
41 | */
42 | public Object getDefaultValue() {
43 | return DefaultValue.forClass(this.getFieldType());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/model/Book.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.model;
2 |
3 | import com.bloggingit.odata.edm.annotation.EdmEntitySet;
4 | import com.bloggingit.odata.edm.annotation.EdmEntityType;
5 | import com.bloggingit.odata.edm.annotation.EdmFacets;
6 | import com.bloggingit.odata.edm.annotation.EdmProperty;
7 | import java.util.Date;
8 | import lombok.AllArgsConstructor;
9 | import lombok.Builder;
10 | import lombok.Getter;
11 | import lombok.NoArgsConstructor;
12 | import lombok.Setter;
13 |
14 | @Getter
15 | @Setter
16 | @NoArgsConstructor
17 | @AllArgsConstructor
18 | @Builder
19 |
20 | @EdmEntityType
21 | @EdmEntitySet
22 | public class Book extends BaseEntity {
23 |
24 | @EdmProperty(facets = @EdmFacets(nullable = false))
25 | private String title;
26 |
27 | @EdmProperty(facets = @EdmFacets(maxLength = 2000))
28 | private String description;
29 |
30 | @EdmProperty(name = "releaseDate")
31 | private Date release;
32 |
33 | @EdmProperty
34 | private Author author;
35 |
36 | @EdmProperty
37 | private Double price;
38 |
39 | @EdmProperty
40 | private boolean inStock;
41 |
42 | public void setRelease(Date release) {
43 | this.release = (release != null) ? new Date(release.getTime()) : null;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/edm/annotation/EdmEntitySet.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.edm.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Annotation for a EDM/CSDL EntitySet element.
10 | *
11 | * EdmEntitySet is the container for entity type instances as described in the
12 | * OData protocol. The {@link EdmEntitySet} annotation defines the annotated
13 | * class as entity set and must be used in conjunction with an
14 | * {@link EdmEntityType} annotation on a class.
15 | *
16 | */
17 | @Retention(RetentionPolicy.RUNTIME)
18 | @Target(ElementType.TYPE)
19 | public @interface EdmEntitySet {
20 |
21 | /**
22 | * Define the name for the EDM Entity Set. If not set a default value has to
23 | * be generated by the EDM provider.
24 | *
25 | * @return name for the EDM Entity Set
26 | */
27 | String name() default "";
28 |
29 | /**
30 | * Define the container name for the EDM Entity Set. If not set a default
31 | * value has to be generated by the EDM provider.
32 | *
33 | * @return container name for the EDM Entity Set
34 | */
35 | String container() default "";
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/util/DefaultValue.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.util;
2 |
3 | import java.lang.reflect.Array;
4 | import java.util.Map;
5 | import java.util.stream.Collectors;
6 | import java.util.stream.Stream;
7 |
8 | /**
9 | * Provides the default value of any primitive type by creating an array of one
10 | * element and retrieving its first value.
11 | */
12 | public class DefaultValue {
13 |
14 | /**
15 | * Map with primitive types
16 | */
17 | private static final Map, Object> DEFAULT_VALUES = Stream
18 | .of(boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, short.class)
19 | .collect(Collectors.toMap(clazz -> (Class>) clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0)));
20 |
21 | /**
22 | * Returns the default value for the given class.
23 | *
24 | * @param the generic type of the class
25 | * @param clazz the class for which a default value is needed
26 | * @return A reasonable default value for the given class (the boxed default
27 | * value for primitives, null otherwise).
28 | */
29 | @SuppressWarnings("unchecked")
30 | public static T forClass(Class clazz) {
31 | return (T) DEFAULT_VALUES.get(clazz);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/edm/enumeration/EdmValueType.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.edm.enumeration;
2 |
3 | import com.bloggingit.odata.edm.annotation.EdmComplexType;
4 | import com.bloggingit.odata.edm.annotation.EdmEntityType;
5 | import java.lang.reflect.Field;
6 | import org.apache.commons.lang3.ClassUtils;
7 |
8 | /**
9 | * Defines the type of a value (see Valuable).
10 | */
11 | public enum EdmValueType {
12 | /**
13 | * Primitive type
14 | */
15 | PRIMITIVE,
16 | /**
17 | * Enum type
18 | */
19 | ENUM,
20 | /**
21 | * Complex type
22 | */
23 | COMPLEX,
24 | /**
25 | * Entity type
26 | */
27 | ENTITY;
28 |
29 |
30 | public static EdmValueType valueOf(Field field) {
31 | Class> type = field.getType();
32 | EdmValueType edmValueType;
33 |
34 | if (type.isEnum()) {
35 | edmValueType = ENUM;
36 | } else if (ClassUtils.isPrimitiveOrWrapper(type)) {
37 | edmValueType = PRIMITIVE;
38 | } else if (type.isAnnotationPresent(EdmComplexType.class)) {
39 | edmValueType = COMPLEX;
40 | } else if (type.isAnnotationPresent(EdmEntityType.class)) {
41 | edmValueType = ENTITY;
42 | } else { // fallback for String, Date, etc.
43 | edmValueType = PRIMITIVE;
44 | }
45 |
46 | return edmValueType;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | #===================================================================
2 | # ::::::::::::::: www.blogging-it.com :::::::::::::::
3 | #
4 | # Copyright (C) 2017 Markus Eschenbach. All rights reserved.
5 | #
6 | #
7 | # This software is provided on an "as-is" basis, without any express or implied warranty.
8 | # In no event shall the author be held liable for any damages arising from the
9 | # use of this software.
10 | #
11 | # Permission is granted to anyone to use this software for any purpose,
12 | # including commercial applications, and to alter and redistribute it,
13 | # provided that the following conditions are met:
14 | #
15 | # 1. All redistributions of source code files must retain all copyright
16 | # notices that are currently in place, and this list of conditions without
17 | # modification.
18 | #
19 | # 2. All redistributions in binary form must retain all occurrences of the
20 | # above copyright notice and web site addresses that are currently in
21 | # place (for example, in the About boxes).
22 | #
23 | # 3. The origin of this software must not be misrepresented; you must not
24 | # claim that you wrote the original software. If you use this software to
25 | # distribute a product, an acknowledgment in the product documentation
26 | # would be appreciated but is not required.
27 | #
28 | # 4. Modified versions in source or binary form must be plainly marked as
29 | # such, and must not be misrepresented as being the original software.
30 | #
31 | # ::::::::::::::: www.blogging-it.com :::::::::::::::
32 | #===================================================================
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/edm/annotation/EdmFacets.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.edm.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | *
10 | * Annotation for definition of EdmFactes on an EdmProperty (for an
11 | * EdmEntityType or EdmComplexType which contains the EdmProperty as a
12 | * field).
13 | *
14 | */
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Target(ElementType.ANNOTATION_TYPE)
17 | public @interface EdmFacets {
18 |
19 | /**
20 | * The maximum length of the type in use. A negative value indicates for the
21 | * EDM provider an unset/default value.
22 | *
23 | * @return the maximum length of the type in use as Integer
24 | */
25 | int maxLength() default -1;
26 |
27 | /**
28 | * The scale of the type in use. A negative value indicates for the EDM
29 | * provider an unset/default value.
30 | *
31 | * @return the scale of the type in use as Integer
32 | */
33 | int scale() default -1;
34 |
35 | /**
36 | * The precision of the type in use. A negative value indicates for the EDM
37 | * provider an unset/default value.
38 | *
39 | * @return the precision of the type in use as Integer
40 | */
41 | int precision() default -1;
42 |
43 | /**
44 | * The information if the type in use is nullable. The default value for
45 | * nullable is true.
46 | *
47 | * @return true if the type in use is nullable,
48 | * false otherwise.
49 | */
50 | boolean nullable() default true;
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/edm/meta/EntityMetaPropertyPrimitve.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.edm.meta;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 |
6 | /**
7 | * This class contains the meta data of a primitive property for the OData
8 | * service.
9 | */
10 | @Getter
11 | public class EntityMetaPropertyPrimitve extends EntityMetaProperty {
12 |
13 | /**
14 | * The maximum length of the type in use as Integer.
15 | *
16 | * A negative value indicates for the EDM provider an unset/default value.
17 | */
18 | private final int maxLength;
19 |
20 | /**
21 | * The precision of the type in use as Integer.
22 | *
23 | * A negative value indicates for the EDM provider an unset/default value.
24 | */
25 | private final int precision;
26 |
27 | /**
28 | * The scale of the type in use as Integer.
29 | *
30 | * A negative value indicates for the EDM provider an unset/default value.
31 | */
32 | private final int scale;
33 |
34 | /**
35 | * true if unicode or null if not specified
36 | */
37 | private final Boolean unicode;
38 |
39 | @Builder
40 | private EntityMetaPropertyPrimitve(
41 | boolean isKey,
42 | String name,
43 | String fieldName,
44 | Class> fieldType,
45 | Boolean nullable,
46 | int maxLength,
47 | int precision,
48 | int scale,
49 | Boolean unicode) {
50 |
51 | super(isKey, name, fieldName, fieldType, nullable);
52 | this.maxLength = maxLength;
53 | this.precision = precision;
54 | this.scale = scale;
55 | this.unicode = unicode;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/mapper/FQNMapper.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.mapper;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaDataContainer;
5 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaProperty;
6 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyComplex;
7 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyEntity;
8 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyEnum;
9 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyPrimitve;
10 | import org.apache.olingo.commons.api.edm.FullQualifiedName;
11 |
12 | /**
13 | *
14 | * @author mes
15 | */
16 | public class FQNMapper {
17 |
18 | public static FullQualifiedName createFullQualifiedName(String serviceNamespace, String name) {
19 | return new FullQualifiedName(serviceNamespace, name);
20 | }
21 |
22 | public static FullQualifiedName createFullQualifiedName(EntityMetaDataContainer entityMetaDataContainer) {
23 | return createFullQualifiedName(entityMetaDataContainer.getServiceNamespace(), entityMetaDataContainer.getEdmContainerName());
24 | }
25 |
26 | public static FullQualifiedName createFullQualifiedName(String serviceNamespace, EntityMetaData meta) {
27 | return createFullQualifiedName(serviceNamespace, meta.getEntityTypeName());
28 | }
29 |
30 |
31 | public static FullQualifiedName mapToPropertyValueTypeFQN(String serviceNamespace, EntityMetaProperty metaProp) {
32 | FullQualifiedName typeFQN = null;
33 | if (metaProp instanceof EntityMetaPropertyEnum || metaProp instanceof EntityMetaPropertyComplex || metaProp instanceof EntityMetaPropertyEntity) {
34 | typeFQN = createFullQualifiedName(serviceNamespace, metaProp.getFieldType().getSimpleName());
35 | } else if (metaProp instanceof EntityMetaPropertyPrimitve) {
36 | typeFQN = OlingoTypeMapper.mapToEdmPrimitiveTypeKind(metaProp.getFieldType()).getFullQualifiedName();
37 | }
38 | return typeFQN;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/util/UriInfoUtil.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.util;
2 |
3 | import java.util.List;
4 | import java.util.Locale;
5 | import org.apache.olingo.commons.api.edm.EdmBindingTarget;
6 | import org.apache.olingo.commons.api.edm.EdmEntitySet;
7 | import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
8 | import org.apache.olingo.commons.api.http.HttpStatusCode;
9 | import org.apache.olingo.server.api.ODataApplicationException;
10 | import org.apache.olingo.server.api.uri.UriInfoResource;
11 | import org.apache.olingo.server.api.uri.UriResource;
12 | import org.apache.olingo.server.api.uri.UriResourceNavigation;
13 |
14 | /**
15 | *
16 | * @author mes
17 | */
18 | public class UriInfoUtil {
19 |
20 | public static EdmEntitySet getNavigationTargetEntitySet(EdmEntitySet startEdmEntitySet, EdmNavigationProperty edmNavigationProperty)
21 | throws ODataApplicationException {
22 |
23 | EdmEntitySet navigationTargetEntitySet = null;
24 |
25 | String navPropName = edmNavigationProperty.getName();
26 | EdmBindingTarget edmBindingTarget = startEdmEntitySet.getRelatedBindingTarget(navPropName);
27 | if (edmBindingTarget == null) {
28 | throw new ODataApplicationException("Not supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
29 | }
30 |
31 | if (edmBindingTarget instanceof EdmEntitySet) {
32 | navigationTargetEntitySet = (EdmEntitySet) edmBindingTarget;
33 | } else {
34 | throw new ODataApplicationException("Not supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
35 | }
36 |
37 | return navigationTargetEntitySet;
38 | }
39 | /**
40 | * Find the last navigation segment
41 | *
42 | * @param uriInfo
43 | * @return
44 | */
45 | public static UriResourceNavigation getLastNavigation(final UriInfoResource uriInfo) {
46 |
47 | final List resourcePaths = uriInfo.getUriResourceParts();
48 | int navigationCount = 1;
49 | while (navigationCount < resourcePaths.size()
50 | && resourcePaths.get(navigationCount) instanceof UriResourceNavigation) {
51 | navigationCount++;
52 | }
53 |
54 | return (UriResourceNavigation) resourcePaths.get(--navigationCount);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/edm/meta/EntityMetaData.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.edm.meta;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import lombok.Getter;
6 |
7 | /**
8 | *
9 | * This class contains the meta data of a entity for the OData service.
10 | *
11 | * @param the generic type of the entity data
12 | */
13 | @Getter
14 | public class EntityMetaData {
15 |
16 | /**
17 | * The suffix for a OData typeset
18 | */
19 | private static final String TYPE_SET_SUFFIX = "Set";
20 |
21 | /**
22 | * The corresponding entity class
23 | */
24 | private final Class entityClass;
25 |
26 | /**
27 | * The name of the entity typeset.
28 | */
29 | private final String entityTypeSetName;
30 |
31 | /**
32 | * The name of the entity type.
33 | */
34 | private final String entityTypeName;
35 |
36 | private final boolean isEntitySet;
37 |
38 | private final boolean isComplexType;
39 | /**
40 | * Contains the list of all properties meta data.
41 | */
42 | private final List properties;
43 |
44 | public EntityMetaData(Class entityClass, String serviceNamespace, boolean isEntitySet, boolean isComplexType, List properties) {
45 | this.entityClass = entityClass;
46 |
47 | //generate values
48 | this.entityTypeName = this.entityClass.getSimpleName();
49 | this.isEntitySet = isEntitySet;
50 | this.isComplexType = isComplexType;
51 |
52 | this.entityTypeSetName = (isEntitySet) ? this.entityTypeName + TYPE_SET_SUFFIX : null;
53 |
54 | this.properties = properties;
55 | }
56 |
57 | public List getEnumPropertyData() {
58 | List enumPropertyList = new ArrayList<>();
59 |
60 | this.properties.stream().filter((prop) -> (prop instanceof EntityMetaPropertyEnum)).forEachOrdered((prop) -> {
61 | enumPropertyList.add(prop);
62 | });
63 | return enumPropertyList;
64 | }
65 |
66 | public List getInlineEntityPropertyData() {
67 | List inlinePropertyList = new ArrayList<>();
68 |
69 | this.properties.stream().filter((prop) -> (prop instanceof EntityMetaPropertyEntity)).forEachOrdered((prop) -> {
70 | inlinePropertyList.add((EntityMetaPropertyEntity) prop);
71 | });
72 | return inlinePropertyList;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/util/ReflectionUtils.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.util;
2 |
3 | import java.beans.IntrospectionException;
4 | import java.beans.PropertyDescriptor;
5 | import java.lang.annotation.Annotation;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.lang.reflect.Method;
8 | import java.util.Set;
9 | import org.apache.olingo.commons.api.ex.ODataRuntimeException;
10 | import org.reflections.Reflections;
11 |
12 | /**
13 | * Simple utility class with convenient helper methods for simple working with
14 | * the java reflection API.
15 | */
16 | public class ReflectionUtils {
17 |
18 | /**
19 | * Get classes annotated with a given annotation in a certain package.
20 | *
21 | * @param dtoPackage search in this certain package
22 | * @param annotationClass find classes annotated with this annotation
23 | * @return the classes with the given annotation
24 | */
25 | public static Set> findClassesInPackageAnnotatedWith(String dtoPackage, Class extends Annotation> annotationClass) {
26 | Reflections reflections = new Reflections(dtoPackage);
27 |
28 | Set> classes = reflections.getTypesAnnotatedWith(annotationClass);
29 |
30 | return classes;
31 |
32 | }
33 |
34 | /**
35 | * Invoke the getter method of the given property for the specified object
36 | *
37 | * @param propertyName invoke the getter of this property
38 | * @param entity invoke the getter for this object
39 | * @return the result of the getter method
40 | */
41 | public static Object invokePropertyGetter(String propertyName, Object entity) {
42 | try {
43 | return new PropertyDescriptor(propertyName, entity.getClass()).getReadMethod().invoke(entity); //invoke getter
44 | } catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
45 | throw new ODataRuntimeException(ex);
46 | }
47 | }
48 |
49 | /**
50 | * Invoke the setter method of the given property for the specified object
51 | * and sets the given value
52 | *
53 | * @param propertyName invoke the setter of this property
54 | * @param entity invoke the setter for this object
55 | * @param val the new value for this property
56 | */
57 | public static void invokePropertySetter(String propertyName, Object entity, Object val) {
58 | try {
59 | Method setMethod = new PropertyDescriptor(propertyName, entity.getClass()).getWriteMethod(); // get setter
60 | setMethod.invoke(entity, val); //invoke setter
61 | } catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NullPointerException ex) {
62 | throw new ODataRuntimeException(ex);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/edm/meta/EntityMetaDataContainer.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.edm.meta;
2 |
3 | import java.util.Collections;
4 | import java.util.List;
5 | import java.util.Set;
6 | import lombok.Getter;
7 |
8 | /**
9 | *
10 | * This class holds all meta data of multiple entities.
11 | */
12 | public class EntityMetaDataContainer {
13 |
14 | @Getter
15 | private final String serviceNamespace;
16 |
17 | @Getter
18 | private final String edmContainerName;
19 |
20 | /**
21 | * List of meta data for multiple entities.
22 | */
23 | @Getter
24 | private final Set> allEntityMetaData;
25 |
26 | public EntityMetaDataContainer(String serviceNamespace, String edmContainerName, Set> allEntityMetaData) {
27 | this.serviceNamespace = serviceNamespace;
28 | this.edmContainerName = edmContainerName;
29 | this.allEntityMetaData = Collections.synchronizedSet(allEntityMetaData);
30 | }
31 |
32 | /**
33 | * Returns the meta entity data of the given type set name. If no set with
34 | * this name found, the method returns null.
35 | *
36 | * @param entityTypeSetName the name of the type set
37 | * @return the meta data of the entity or null if no set found
38 | */
39 | public EntityMetaData> getEntityMetaDataByTypeSetName(String entityTypeSetName) {
40 | EntityMetaData> entity = null;
41 | for (EntityMetaData> meta : this.allEntityMetaData) {
42 | if (meta.getEntityTypeSetName() != null && meta.getEntityTypeSetName().equals(entityTypeSetName)) {
43 | entity = meta;
44 | break;
45 | }
46 | }
47 | return entity;
48 | }
49 |
50 | /**
51 | * Returns the meta entity data of the given full qualified type name. If no
52 | * meta data with this name found, the method returns null.
53 | *
54 | * @param serviceNamespace the service namespace
55 | * @param entityTypeName the name of the type name
56 | * @return the meta data of the entity or null if no meta data
57 | * found
58 | */
59 | public EntityMetaData> getEntityMetaDataByTypeName(String serviceNamespace, String entityTypeName) {
60 |
61 | EntityMetaData> entity = null;
62 |
63 | if (this.serviceNamespace.equals(serviceNamespace)) {
64 | for (EntityMetaData> meta : this.allEntityMetaData) {
65 | if (meta.getEntityTypeName().equals(entityTypeName)) {
66 | entity = meta;
67 | break;
68 | }
69 | }
70 | }
71 | return entity;
72 | }
73 |
74 | public EntityMetaProperty getEntityMetaPropertyDataByTypeName(String serviceNamespace, String propertyTypeName) {
75 | EntityMetaProperty propData = null;
76 | if (this.serviceNamespace.equals(serviceNamespace)) {
77 | for (EntityMetaData> meta : this.allEntityMetaData) {
78 | List enumPropertyDataList = meta.getEnumPropertyData();
79 |
80 | for (EntityMetaProperty propEntry : enumPropertyDataList) {
81 | if (propEntry.getFieldType().getSimpleName().equals(propertyTypeName)) {
82 | propData = propEntry;
83 | break;
84 | }
85 |
86 | }
87 |
88 | }
89 | }
90 | return propData;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/processor/AbstractEntityMetaDataProcessor.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.processor;
2 |
3 | import java.io.InputStream;
4 | import java.util.List;
5 | import java.util.Locale;
6 | import org.apache.olingo.commons.api.edm.EdmEntitySet;
7 | import org.apache.olingo.commons.api.edm.EdmProperty;
8 | import org.apache.olingo.commons.api.format.ContentType;
9 | import org.apache.olingo.commons.api.http.HttpHeader;
10 | import org.apache.olingo.commons.api.http.HttpStatusCode;
11 | import org.apache.olingo.server.api.ODataApplicationException;
12 | import org.apache.olingo.server.api.ODataResponse;
13 | import org.apache.olingo.server.api.uri.UriInfoResource;
14 | import org.apache.olingo.server.api.uri.UriParameter;
15 | import org.apache.olingo.server.api.uri.UriResource;
16 | import org.apache.olingo.server.api.uri.UriResourceEntitySet;
17 | import org.apache.olingo.server.api.uri.UriResourceProperty;
18 |
19 | /**
20 | *
21 | * @author mes
22 | */
23 | public abstract class AbstractEntityMetaDataProcessor {
24 |
25 | protected void setResponseContentAndOkStatus(ODataResponse response, InputStream content, ContentType responseFormat) {
26 | response.setContent(content);
27 | response.setStatusCode(HttpStatusCode.OK.getStatusCode());
28 | response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
29 | }
30 |
31 | protected void setResponseNoContentStatus(ODataResponse response) {
32 | response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
33 | }
34 |
35 | protected EdmEntitySet getUriResourceEdmEntitySet(UriInfoResource uriInfo) throws ODataApplicationException {
36 | return getUriResourceEntitySet(uriInfo).getEntitySet();
37 | }
38 |
39 | protected List getUriResourceKeyPredicates(UriInfoResource uriInfo) throws ODataApplicationException {
40 | return getUriResourceEntitySet(uriInfo).getKeyPredicates();
41 | }
42 |
43 | protected UriResourceEntitySet getUriResourceEntitySet(UriInfoResource uriInfo) throws ODataApplicationException {
44 | List resourceParts = uriInfo.getUriResourceParts();
45 | // To get the entity set we have to interpret all URI segments
46 | if (!(resourceParts.get(0) instanceof UriResourceEntitySet)) {
47 | // Here we should interpret the whole URI but in this example we do not support navigation so we throw an exception
48 | throw new ODataApplicationException("Invalid resource type for first segment.", HttpStatusCode.NOT_IMPLEMENTED
49 | .getStatusCode(), Locale.ENGLISH);
50 | }
51 |
52 | return (UriResourceEntitySet) resourceParts.get(0);
53 | }
54 |
55 | protected EdmProperty getUriResourceEdmProperty(UriInfoResource uriInfo) throws ODataApplicationException {
56 |
57 | List resourceParts = uriInfo.getUriResourceParts();
58 | // the last segment is the Property
59 | int idx = resourceParts.size() - 1;
60 |
61 | if (!(resourceParts.get(idx) instanceof UriResourceProperty)) {
62 | // Here we should interpret the whole URI but in this example we do not support navigation so we throw an exception
63 | throw new ODataApplicationException("Invalid resource type for last segment.", HttpStatusCode.NOT_IMPLEMENTED
64 | .getStatusCode(), Locale.ENGLISH);
65 | }
66 |
67 | UriResourceProperty uriProperty = (UriResourceProperty) resourceParts.get(idx);
68 | return uriProperty.getProperty();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # Build an Apache TomEE container and deploy the WAR-Archives
3 | #
4 | # ##################### INFOS ######################
5 | # - Config path: /usr/local/tomee/conf
6 | # - Webapp path: /usr/local/tomee/webapps
7 | # - User for the Tomcat Manager Application: tomee:tomee
8 | #
9 | # - ROOT App: /admin e.g. http://localhost:8080/admin
10 | # - Quickstart App: /angular2-qickstart-javaee7-application e.g. http://localhost:8080/angular2-qickstart-javaee7-application
11 | # - Quickstart Angular: /angular2-qickstart-javaee7-client-web e.g. http://localhost:8080/angular2-qickstart-javaee7-client-web
12 | # ##################### INFOS ######################
13 | #
14 | #
15 | # ##################### DOCKER #####################
16 | # BUILD
17 | # docker build -t angular2/javaee7-quickstart:1.0.0 .
18 | #
19 | # RUN (add -d parameter to start a container in detached mode)
20 | # docker run -P -it --rm -p 8080:8080 --name angular2-javaee7-quickstart angular2/javaee7-quickstart:1.0.0
21 | #
22 | # STOP
23 | # docker stop angular2-javaee7-quickstart
24 | #
25 | # REMOVE CONTAINER AND IMAGE
26 | # docker rm -f angular2-javaee7-quickstart ; docker rmi -f angular2/javaee7-quickstart:1.0.0
27 | #
28 | # LOGIN INTO CONTAINER
29 | # docker exec -i -t angular2-javaee7-quickstart /bin/bash
30 | #
31 | # COPY FILE FROM CONTAINER TO HOST
32 | # docker cp angular2-javaee7-quickstart:/usr/local/tomee/conf/web.xml ./web.xml
33 | #
34 | # ##################### DOCKER #####################
35 | #
36 | #
37 |
38 | # build file based on TomEE 7.0.1 Plume
39 | FROM tomee:8-jdk-7.0.1-plume
40 | MAINTAINER "Markus Eschenbach "
41 |
42 |
43 | # ************* MODIFY CONTAINER FILES *************
44 |
45 | # rename ROOT application folder to admin
46 | RUN mv /usr/local/tomee/webapps/ROOT /usr/local/tomee/webapps/admin
47 |
48 | # create new ROOT application folder and index.jsp welcome file
49 | RUN mkdir /usr/local/tomee/webapps/ROOT && echo "It works!" >> /usr/local/tomee/webapps/ROOT/index.jsp
50 |
51 | # add admin user tomee:tomee
52 | RUN sed -i '/<\/tomcat-users>/d' /usr/local/tomee/conf/tomcat-users.xml
53 | RUN echo " \
54 | \
55 | \
56 | \
57 | " >> /usr/local/tomee/conf/tomcat-users.xml
58 |
59 |
60 | # **************** INSTALL TOOLS *****************
61 |
62 | # Avoid warnings during installsets
63 | # variable only live during the build process
64 | ARG DEBIAN_FRONTEND=noninteractive
65 |
66 | # Installing the 'apt-utils' package gets rid of the 'debconf: delaying package configuration, since apt-utils is not installed'
67 | # error message when installing any other package with the apt-get package manager.
68 | RUN apt-get update \
69 | && apt-get install -y --no-install-recommends \
70 | apt-utils \
71 | && rm -rf /var/lib/apt/lists/*
72 |
73 | # make sure the package repository is up to date and install tools like nano etc.
74 | RUN apt-get update \
75 | && apt-get install -y --no-install-recommends \
76 | nano
77 |
78 |
79 | # *************** ADD APPLICATIONS ***************
80 |
81 | ADD module-application/target/angular2-qickstart-javaee7-application.war /usr/local/tomee/webapps/angular2-qickstart-javaee7-application.war
82 | ADD module-client-web/target/angular2-qickstart-javaee7-client-web.war /usr/local/tomee/webapps/angular2-qickstart-javaee7-client-web.war
83 |
84 |
85 | # ******************** EXPOSE ********************
86 |
87 | EXPOSE 8080
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/mapper/OlingoTypeMapper.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.mapper;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaProperty;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyComplex;
5 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyEntity;
6 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyEnum;
7 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyPrimitve;
8 | import java.math.BigDecimal;
9 | import java.math.BigInteger;
10 | import java.util.Arrays;
11 | import java.util.Calendar;
12 | import java.util.Date;
13 | import java.util.HashMap;
14 | import java.util.List;
15 | import java.util.Map;
16 | import java.util.UUID;
17 | import org.apache.olingo.commons.api.data.ValueType;
18 | import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
19 |
20 | public class OlingoTypeMapper {
21 |
22 | /**
23 | * Maps classes to their corresponding ed, primitive {@code Class}es
24 | */
25 | public final static Map>, EdmPrimitiveTypeKind> CLASS_TO_TYPE_KIND_MAP = new HashMap<>();
26 |
27 | static {
28 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(String.class), EdmPrimitiveTypeKind.String);
29 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(boolean.class, Boolean.class), EdmPrimitiveTypeKind.Boolean);
30 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(byte.class, Byte.class), EdmPrimitiveTypeKind.SByte);
31 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(byte[].class, Byte[].class), EdmPrimitiveTypeKind.Binary);
32 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(short.class, Short.class), EdmPrimitiveTypeKind.Int16);
33 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(int.class, Integer.class), EdmPrimitiveTypeKind.Int32);
34 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(long.class, Long.class), EdmPrimitiveTypeKind.Int64);
35 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(float.class, Float.class), EdmPrimitiveTypeKind.Single);
36 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(double.class, Double.class), EdmPrimitiveTypeKind.Double);
37 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(BigInteger.class, BigDecimal.class), EdmPrimitiveTypeKind.Decimal);
38 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(Date.class), EdmPrimitiveTypeKind.Date);
39 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(Calendar.class), EdmPrimitiveTypeKind.DateTimeOffset);
40 | CLASS_TO_TYPE_KIND_MAP.put(Arrays.asList(UUID.class), EdmPrimitiveTypeKind.Guid);
41 |
42 | }
43 |
44 | public static ValueType mapToValueType(EntityMetaProperty metaProp) {
45 | ValueType valType = null;
46 |
47 | if (metaProp instanceof EntityMetaPropertyPrimitve) {
48 | valType = ValueType.PRIMITIVE;
49 | } else if (metaProp instanceof EntityMetaPropertyEnum) {
50 | valType = ValueType.ENUM;
51 | } else if (metaProp instanceof EntityMetaPropertyComplex) {
52 | valType = ValueType.COMPLEX;
53 | } else if (metaProp instanceof EntityMetaPropertyEntity) {
54 | valType = ValueType.ENTITY;
55 | }
56 |
57 | return valType;
58 | }
59 |
60 | public static EdmPrimitiveTypeKind mapToEdmPrimitiveTypeKind(Class> fieldType) {
61 | EdmPrimitiveTypeKind converted = null;
62 |
63 | for (Map.Entry>, EdmPrimitiveTypeKind> entry : CLASS_TO_TYPE_KIND_MAP.entrySet()) {
64 | if (entry.getKey().contains(fieldType)) {
65 | converted = entry.getValue();
66 | break;
67 | }
68 | }
69 |
70 | if (converted == null) {
71 | throw new UnsupportedOperationException("Not a supported type '" + fieldType + "'.");
72 | }
73 |
74 | return converted;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/servlet/ODataDemoServlet.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.servlet;
2 |
3 | import java.io.IOException;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import javax.servlet.ServletConfig;
7 | import javax.servlet.ServletException;
8 | import javax.servlet.annotation.WebServlet;
9 | import javax.servlet.http.HttpServlet;
10 | import javax.servlet.http.HttpServletRequest;
11 | import javax.servlet.http.HttpServletResponse;
12 | import org.apache.olingo.server.api.OData;
13 | import org.apache.olingo.server.api.ODataHttpHandler;
14 | import org.apache.olingo.server.api.ServiceMetadata;
15 |
16 | import com.bloggingit.odata.olingo.v4.provider.AnnotationEdmProvider;
17 | import com.bloggingit.odata.olingo.v4.processor.DataCollectionProcessor;
18 | import com.bloggingit.odata.olingo.v4.processor.DataEntityProcessor;
19 | import com.bloggingit.odata.olingo.v4.processor.DataPrimitiveValueProcessor;
20 | import com.bloggingit.odata.olingo.v4.service.OlingoDataService;
21 | import javax.inject.Inject;
22 | import javax.servlet.Servlet;
23 | import org.apache.olingo.commons.api.edmx.EdmxReference;
24 | import org.apache.olingo.server.api.debug.DefaultDebugSupport;
25 |
26 | /**
27 | * This {@link Servlet} provides the OData service.
28 | *
29 | * @author mes
30 | */
31 | @WebServlet(name = ODataDemoServlet.SERVLET_NAME, urlPatterns = {ODataDemoServlet.SERVLET_URL_PATTERNS})
32 | public class ODataDemoServlet extends HttpServlet {
33 |
34 | private static final long serialVersionUID = 1L;
35 |
36 | public static final String SERVLET_NAME = "ODataDemoServlet";
37 |
38 | public static final String SERVLET_URL_PATTERNS = "/api/servlet/v1/odatademo.svc/*";
39 |
40 | private static final String BASE_MODEL_PACKAGE = "com.bloggingit.odata.model";
41 |
42 | public static final String SERVICE_NAMESPACE = "OData";
43 |
44 | public static final String EDM_CONTAINER_NAME = "Container";
45 |
46 | private transient AnnotationEdmProvider edmProvider;
47 | private transient DataCollectionProcessor entityCollectionProcessor;
48 | private transient DataEntityProcessor entityProcessor;
49 | //private transient DataPrimitiveProcessor entityDataPrimitiveProcessor;
50 | private transient DataPrimitiveValueProcessor entityDataPrimitiveValueProcessor;
51 |
52 | @Inject
53 | private OlingoDataService dataService;
54 |
55 | @Override
56 | public void init(ServletConfig config) throws ServletException {
57 | super.init(config);
58 |
59 | this.edmProvider = new AnnotationEdmProvider(SERVICE_NAMESPACE, EDM_CONTAINER_NAME, BASE_MODEL_PACKAGE);
60 | this.entityCollectionProcessor = new DataCollectionProcessor(dataService, this.edmProvider.getEntityMetaDataContainer());
61 | this.entityProcessor = new DataEntityProcessor(dataService, this.edmProvider.getEntityMetaDataContainer());
62 | // this.entityDataPrimitiveProcessor = new DataPrimitiveProcessor(dataService, this.edmProvider.getEntityMetaDataCollection());
63 | this.entityDataPrimitiveValueProcessor = new DataPrimitiveValueProcessor(dataService, this.edmProvider.getEntityMetaDataContainer());
64 | }
65 |
66 | @Override
67 | protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
68 |
69 | List odataProcessors = new ArrayList<>();
70 | OData odata = OData.newInstance();
71 | ServiceMetadata edm = odata.createServiceMetadata(this.edmProvider, odataProcessors);
72 | ODataHttpHandler handler = odata.createHandler(edm);
73 | handler.register(entityCollectionProcessor);
74 | handler.register(entityProcessor);
75 | //handler.register(entityDataPrimitiveProcessor);
76 | handler.register(entityDataPrimitiveValueProcessor);
77 |
78 | //run service url query option odata-debug=json to return detailed error information in json format for each request.
79 | //http://olingo.apache.org/doc/odata2/tutorials/debug.html
80 | handler.register(new DefaultDebugSupport());
81 |
82 | handler.process(req, resp);
83 |
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/etc/docker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #===================================================================
4 | #
5 | # -- VERSION 1.0.0 --
6 | #
7 | # Startparameter:
8 | # build | start | startbg | stop | remove | login
9 | #
10 | # Example command:
11 | # ./docker.sh build
12 | #
13 | # ::::::::::::::: www.blogging-it.com :::::::::::::::
14 | #
15 | # Copyright (C) 2017 Markus Eschenbach. All rights reserved.
16 | #
17 | #
18 | # This software is provided on an "as-is" basis, without any express or implied warranty.
19 | # In no event shall the author be held liable for any damages arising from the
20 | # use of this software.
21 | #
22 | # Permission is granted to anyone to use this software for any purpose,
23 | # including commercial applications, and to alter and redistribute it,
24 | # provided that the following conditions are met:
25 | #
26 | # 1. All redistributions of source code files must retain all copyright
27 | # notices that are currently in place, and this list of conditions without
28 | # modification.
29 | #
30 | # 2. All redistributions in binary form must retain all occurrences of the
31 | # above copyright notice and web site addresses that are currently in
32 | # place (for example, in the About boxes).
33 | #
34 | # 3. The origin of this software must not be misrepresented; you must not
35 | # claim that you wrote the original software. If you use this software to
36 | # distribute a product, an acknowledgment in the product documentation
37 | # would be appreciated but is not required.
38 | #
39 | # 4. Modified versions in source or binary form must be plainly marked as
40 | # such, and must not be misrepresented as being the original software.
41 | #
42 | # ::::::::::::::: www.blogging-it.com :::::::::::::::
43 | #===================================================================
44 |
45 |
46 | #===================================================================
47 | # SETTINGS
48 | #===================================================================
49 | BASE_DIR="$(dirname $0)"
50 | SCRIPT_DIR="$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )"
51 | WORK_DIR="$SCRIPT_DIR/.."
52 | WAR_APP_PATH="$WORK_DIR/module-application/target/angular2-qickstart-javaee7-application.war"
53 | WAR_CLIENT_PATH="$WORK_DIR/module-client-web/target/angular2-qickstart-javaee7-client-web.war"
54 |
55 | DOCKER_VERSION="1.0.0"
56 | DOCKER_EXEC="docker"
57 | DOCKER_IMAGE="angular2/javaee7-quickstart"
58 | DOCKER_NAME="angular2-javaee7-quickstart"
59 | DOCKER_PORT="8080:8080"
60 |
61 | ACTION="$1"
62 |
63 | #===================================================================
64 | # FUNCTIONS
65 | #===================================================================
66 |
67 | function isAppInstalled {
68 | local isInst_=1
69 | type $1 >/dev/null 2>&1 || { local isInst_=0; } # set to 0 if not found
70 | echo "$isInst_"
71 | }
72 |
73 |
74 | #===================================================================
75 | # MAIN
76 | #===================================================================
77 |
78 | valid=true
79 |
80 | if [[ $(isAppInstalled docker) != 1 ]]; then
81 | echo "ERROR: Please install 'docker' to execute this script"
82 | valid=false
83 | fi
84 |
85 | if [ ! -f "$WAR_APP_PATH" ]; then
86 | echo "ERROR: file '$WAR_APP_PATH' missing! Please execute maven build..."
87 | valid=false
88 | fi
89 |
90 | if [ ! -f "$WAR_CLIENT_PATH" ]; then
91 | echo "ERROR: file '$WAR_CLIENT_PATH' missing! Please execute maven build..."
92 | valid=false
93 | fi
94 |
95 | if [ "$valid" = false ] ; then
96 | exit 1
97 | fi
98 |
99 |
100 | EXEC_CMD=""
101 | case "$ACTION" in
102 | build)
103 | EXEC_CMD="$DOCKER_EXEC build -t $DOCKER_IMAGE:$DOCKER_VERSION ."
104 | ;;
105 |
106 | start)
107 | EXEC_CMD="$DOCKER_EXEC run -P -it --rm -p $DOCKER_PORT --name $DOCKER_NAME $DOCKER_IMAGE:$DOCKER_VERSION"
108 | ;;
109 |
110 | startbg)
111 | EXEC_CMD="$DOCKER_EXEC run -P -d -it --rm -p $DOCKER_PORT --name $DOCKER_NAME $DOCKER_IMAGE:$DOCKER_VERSION"
112 | ;;
113 |
114 | stop)
115 | EXEC_CMD="$DOCKER_EXEC stop $DOCKER_NAME"
116 | ;;
117 |
118 | remove)
119 | EXEC_CMD="$DOCKER_EXEC rm -f $DOCKER_NAME ; docker rmi -f $DOCKER_IMAGE:$DOCKER_VERSION"
120 | ;;
121 |
122 | login)
123 | EXEC_CMD="$DOCKER_EXEC exec -i -t $DOCKER_NAME /bin/bash"
124 | ;;
125 |
126 | *)
127 | echo $"Usage: $ACTION {build|start|startbg|stop|remove|login}"
128 | exit 1
129 | esac
130 |
131 | cd "$WORK_DIR"
132 |
133 | echo "Run '$EXEC_CMD'"
134 | eval $EXEC_CMD
135 |
136 |
137 | exit 0
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/processor/DataPrimitiveProcessor.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.processor;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaDataContainer;
5 | import com.bloggingit.odata.olingo.v4.service.OlingoDataService;
6 | import java.io.InputStream;
7 | import java.util.List;
8 | import java.util.Locale;
9 | import org.apache.olingo.commons.api.data.ContextURL;
10 | import org.apache.olingo.commons.api.data.Entity;
11 | import org.apache.olingo.commons.api.data.Property;
12 | import org.apache.olingo.commons.api.edm.EdmEntitySet;
13 | import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
14 | import org.apache.olingo.commons.api.edm.EdmProperty;
15 | import org.apache.olingo.commons.api.format.ContentType;
16 | import org.apache.olingo.commons.api.http.HttpStatusCode;
17 | import org.apache.olingo.server.api.OData;
18 | import org.apache.olingo.server.api.ODataApplicationException;
19 | import org.apache.olingo.server.api.ODataLibraryException;
20 | import org.apache.olingo.server.api.ODataRequest;
21 | import org.apache.olingo.server.api.ODataResponse;
22 | import org.apache.olingo.server.api.ServiceMetadata;
23 | import org.apache.olingo.server.api.processor.PrimitiveProcessor;
24 | import org.apache.olingo.server.api.serializer.ODataSerializer;
25 | import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
26 | import org.apache.olingo.server.api.serializer.SerializerResult;
27 | import org.apache.olingo.server.api.uri.UriInfo;
28 | import org.apache.olingo.server.api.uri.UriParameter;
29 |
30 | /**
31 | * This class is invoked by the Apache Olingo framework when the the OData
32 | * service is invoked order to display primitive property data of a entity.
33 | */
34 | public class DataPrimitiveProcessor extends AbstractEntityMetaDataProcessor implements PrimitiveProcessor {
35 |
36 | private OData odata;
37 | private ServiceMetadata serviceMetadata;
38 | private final EntityMetaDataContainer entityMetaDataCollection;
39 |
40 | private final OlingoDataService dataService;
41 |
42 | public DataPrimitiveProcessor(OlingoDataService dataService, EntityMetaDataContainer entityMetaDataCollection) {
43 | this.dataService = dataService;
44 | this.entityMetaDataCollection = entityMetaDataCollection;
45 | }
46 |
47 | @Override
48 | public void init(OData odata, ServiceMetadata serviceMetadata) {
49 | this.odata = odata;
50 | this.serviceMetadata = serviceMetadata;
51 | }
52 |
53 | @Override
54 | public void readPrimitive(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
55 | // 1. Retrieve info from URI
56 | // 1.1. retrieve the info about the requested entity set
57 | EdmEntitySet edmEntitySet = getUriResourceEdmEntitySet(uriInfo);
58 | // the key for the entity
59 | List keyPredicates = getUriResourceKeyPredicates(uriInfo);
60 |
61 | // 1.2. retrieve the requested (Edm) property
62 | EdmProperty edmProperty = getUriResourceEdmProperty(uriInfo);
63 | String edmPropertyName = edmProperty.getName();
64 | // in our example, we know we have only primitive types in our model
65 | EdmPrimitiveType edmPropertyType = (EdmPrimitiveType) edmProperty.getType();
66 |
67 | // 2. retrieve data from backend
68 | // 2.1. retrieve the entity data, for which the property has to be read
69 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
70 |
71 | Entity entity = this.dataService.getEntityData(meta, keyPredicates);
72 |
73 | if (entity == null) { // Bad request
74 | throw new ODataApplicationException("Entity not found",
75 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
76 | }
77 |
78 | // 2.2. retrieve the property data from the entity
79 | Property property = entity.getProperty(edmPropertyName);
80 | if (property == null) {
81 | throw new ODataApplicationException("Property not found",
82 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
83 | }
84 |
85 | // 3. serialize
86 | Object value = property.getValue();
87 | if (value != null) {
88 | // 3.1. configure the serializer
89 | ODataSerializer serializer = odata.createSerializer(responseFormat);
90 |
91 | ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).navOrPropertyPath(edmPropertyName).build();
92 | PrimitiveSerializerOptions options = PrimitiveSerializerOptions.with().contextURL(contextUrl).build();
93 | // 3.2. serialize
94 | SerializerResult serializerResult = serializer.primitive(serviceMetadata, edmPropertyType, property, options);
95 | InputStream propertyStream = serializerResult.getContent();
96 |
97 | //4. configure the response object
98 | setResponseContentAndOkStatus(response, propertyStream, responseFormat);
99 | } else {
100 | // in case there's no value for the property, we can skip the serialization
101 | setResponseNoContentStatus(response);
102 | }
103 | }
104 |
105 | @Override
106 | public void updatePrimitive(ODataRequest odr, ODataResponse odr1, UriInfo ui, ContentType ct, ContentType ct1) throws ODataApplicationException, ODataLibraryException {
107 | throw new UnsupportedOperationException("Not supported yet.");
108 | }
109 |
110 | @Override
111 | public void deletePrimitive(ODataRequest odr, ODataResponse odr1, UriInfo ui) throws ODataApplicationException, ODataLibraryException {
112 | throw new UnsupportedOperationException("Not supported yet.");
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/service/OlingoDataService.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.service;
2 |
3 | import com.bloggingit.odata.model.BaseEntity;
4 | import com.bloggingit.odata.exception.EntityDataException;
5 | import com.bloggingit.odata.olingo.v4.mapper.OlingoObjectMapper;
6 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
7 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaProperty;
8 | import com.bloggingit.odata.storage.InMemoryDataStorage;
9 | import java.io.Serializable;
10 | import java.util.HashMap;
11 | import java.util.List;
12 | import java.util.Locale;
13 | import java.util.Map;
14 | import javax.ejb.LocalBean;
15 | import javax.ejb.Stateless;
16 | import org.apache.olingo.commons.api.data.Entity;
17 | import org.apache.olingo.commons.api.data.EntityCollection;
18 | import org.apache.olingo.commons.api.data.Link;
19 | import org.apache.olingo.commons.api.data.Property;
20 | import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
21 | import org.apache.olingo.commons.api.http.HttpMethod;
22 | import org.apache.olingo.commons.api.http.HttpStatusCode;
23 | import org.apache.olingo.server.api.ODataApplicationException;
24 | import org.apache.olingo.server.api.uri.UriParameter;
25 | import org.apache.olingo.server.api.uri.UriResourceNavigation;
26 |
27 | /**
28 | * This service provides the methods for the OData service to read and store
29 | * data.
30 | *
31 | * Internally the in-memory data storage will be used.
32 | */
33 | @Stateless
34 | @LocalBean
35 | public class OlingoDataService implements Serializable {
36 |
37 | private static final long serialVersionUID = 1L;
38 |
39 | public EntityCollection getEntityDataList(EntityMetaData entityMetaData) {
40 | List entityDataList = InMemoryDataStorage.getDataListByBaseEntityClass(entityMetaData.getEntityClass());
41 |
42 | return OlingoObjectMapper.mapObjectEntitiesToOlingoEntityCollection(entityDataList, entityMetaData);
43 |
44 | }
45 |
46 | public Entity getEntityData(EntityMetaData entityMetaData, List keyParams) {
47 | long id = Long.parseLong(keyParams.get(0).getText());
48 | T baseEntity = InMemoryDataStorage.getDataByClassAndId(entityMetaData.getEntityClass(), id);
49 | return (baseEntity != null) ? OlingoObjectMapper.mapObjEntityToOlingoEntity(entityMetaData, baseEntity) : null;
50 | }
51 |
52 | @SuppressWarnings("unchecked")
53 | public void deleteEntityData(Class> entityClass, List keyParams) {
54 | long id = Long.parseLong(keyParams.get(0).getText());
55 |
56 | InMemoryDataStorage.deleteDataByClassAndId((Class) entityClass, id);
57 | }
58 |
59 | public Entity createEntityData(EntityMetaData entityMetaData, Entity requestEntity) throws ODataApplicationException {
60 |
61 | T baseEntity = OlingoObjectMapper.mapOlingoEntityToObjectEntity(entityMetaData, requestEntity);
62 | T newBaseEntity;
63 | try {
64 | newBaseEntity = InMemoryDataStorage.createEntity(baseEntity);
65 | } catch (EntityDataException ex) {
66 | throw new ODataApplicationException("Entity not found",
67 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH, ex);
68 | }
69 |
70 | return OlingoObjectMapper.mapObjEntityToOlingoEntity(entityMetaData, newBaseEntity);
71 | }
72 |
73 | public Entity getRelatedEntity(Entity entity, UriResourceNavigation navigationResource)
74 | throws ODataApplicationException {
75 |
76 | final EdmNavigationProperty edmNavigationProperty = navigationResource.getProperty();
77 |
78 | //if (edmNavigationProperty.isCollection()) {
79 | // return Util.findEntity(edmNavigationProperty.getType(), getRelatedEntityCollection(entity, navigationResource),
80 | // navigationResource.getKeyPredicates());
81 | //} else {
82 | final Link link = entity.getNavigationLink(edmNavigationProperty.getName());
83 | return link == null ? null : link.getInlineEntity();
84 | // }
85 | }
86 |
87 | public void updateEntityData(EntityMetaData> entityMetaData, List keyParams, Entity entity, HttpMethod httpMethod) throws ODataApplicationException {
88 | long id = Long.parseLong(keyParams.get(0).getText());
89 |
90 | Map newPropertiesAndValues = new HashMap<>();
91 |
92 | // depending on the HttpMethod, our behavior is different
93 | // in case of PATCH, the existing property is not touched, do nothing
94 | //in case of PUT, the existing property is set to null
95 | boolean nullableUnkownProperties = (httpMethod.equals(HttpMethod.PUT));
96 |
97 | List metaProperties = entityMetaData.getProperties();
98 |
99 | metaProperties.forEach((metaProp) -> {
100 | Property newProperty = entity.getProperty(metaProp.getEdmName());
101 |
102 | if (newProperty != null && !metaProp.isKey()) {
103 | Object val = OlingoObjectMapper.mapOlingoPropertyToObjValue(newProperty, metaProp);
104 | newPropertiesAndValues.put(metaProp.getFieldName(), val);
105 | } else if (nullableUnkownProperties && !metaProp.isKey()) {
106 | // if a property has NOT been added to the request payload
107 | // depending on the HttpMethod, our behavior is different
108 | // in case of PUT, the existing property is set to null
109 | // in case of PATCH, the existing property is not touched, do nothing
110 | newPropertiesAndValues.put(metaProp.getFieldName(), metaProp.getDefaultValue());
111 | }
112 | });
113 |
114 | try {
115 | InMemoryDataStorage.updateEntity(entityMetaData.getEntityClass(), id, newPropertiesAndValues, nullableUnkownProperties);
116 | } catch (EntityDataException ex) {
117 | throw new ODataApplicationException("Entity not found",
118 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH, ex);
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/processor/DataCollectionProcessor.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.processor;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaDataContainer;
5 | import com.bloggingit.odata.olingo.v4.service.OlingoDataService;
6 | import java.io.ByteArrayInputStream;
7 | import java.nio.charset.Charset;
8 |
9 | import org.apache.olingo.commons.api.data.ContextURL;
10 | import org.apache.olingo.commons.api.data.EntityCollection;
11 | import org.apache.olingo.commons.api.edm.EdmEntitySet;
12 | import org.apache.olingo.commons.api.edm.EdmEntityType;
13 | import org.apache.olingo.commons.api.format.ContentType;
14 | import org.apache.olingo.server.api.OData;
15 | import org.apache.olingo.server.api.ODataApplicationException;
16 | import org.apache.olingo.server.api.ODataLibraryException;
17 | import org.apache.olingo.server.api.ODataRequest;
18 | import org.apache.olingo.server.api.ODataResponse;
19 | import org.apache.olingo.server.api.ServiceMetadata;
20 | import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor;
21 | import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
22 | import org.apache.olingo.server.api.serializer.ODataSerializer;
23 | import org.apache.olingo.server.api.serializer.SerializerException;
24 | import org.apache.olingo.server.api.serializer.SerializerResult;
25 | import org.apache.olingo.server.api.uri.UriInfo;
26 | import org.apache.olingo.server.api.uri.queryoption.CountOption;
27 |
28 | /**
29 | * This class is invoked by the Apache Olingo framework when the the OData
30 | * service is invoked order to display a list/collection of data (entities).
31 | *
32 | * This is the case if an EntitySet is requested by the user.
33 | */
34 | public class DataCollectionProcessor extends AbstractEntityMetaDataProcessor implements CountEntityCollectionProcessor {
35 |
36 | private OData odata;
37 | private ServiceMetadata serviceMetadata;
38 | private final EntityMetaDataContainer entityMetaDataCollection;
39 |
40 | private final OlingoDataService dataService;
41 |
42 | public DataCollectionProcessor(OlingoDataService dataService, EntityMetaDataContainer entityMetaDataCollection) {
43 | this.dataService = dataService;
44 | this.entityMetaDataCollection = entityMetaDataCollection;
45 | }
46 |
47 | @Override
48 | public void init(OData odata, ServiceMetadata serviceMetadata) {
49 | this.odata = odata;
50 | this.serviceMetadata = serviceMetadata;
51 | }
52 |
53 | @Override
54 | public void readEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, SerializerException {
55 |
56 | // 1st we have retrieve the requested EntitySet from the uriInfo object (representation of the parsed service URI)
57 | EdmEntitySet edmEntitySet = getUriResourceEdmEntitySet(uriInfo);
58 |
59 | // 2nd: fetch the data from backend for this requested EntitySetName
60 | // it has to be delivered as EntitySet object
61 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
62 |
63 | EntityCollection entitySet = dataService.getEntityDataList(meta);
64 |
65 | //The $count system query option with a value of true specifies that the total count
66 | //of items within a collection matching the request be returned along with the result.
67 | CountOption countOption = uriInfo.getCountOption();
68 | if (countOption != null) {
69 | boolean isCount = countOption.getValue();
70 | if (isCount) {
71 | entitySet.setCount(entitySet.getEntities().size());
72 | }
73 | }
74 |
75 |
76 | // 3rd: create a serializer based on the requested format (json)
77 | ODataSerializer serializer = odata.createSerializer(responseFormat);
78 |
79 | // 4th: Now serialize the content: transform from the EntitySet object to InputStream
80 | EdmEntityType edmEntityType = edmEntitySet.getEntityType();
81 | ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build();
82 |
83 | final String id = request.getRawBaseUri() + "/" + edmEntitySet.getName();
84 | EntityCollectionSerializerOptions opts
85 | = EntityCollectionSerializerOptions
86 | .with()
87 | .id(id)
88 | .count(countOption)
89 | .contextURL(contextUrl)
90 | .build();
91 | SerializerResult serializedContent = serializer.entityCollection(serviceMetadata, edmEntityType, entitySet, opts);
92 |
93 | // Finally: configure the response object: set the body, headers and status code
94 | setResponseContentAndOkStatus(response, serializedContent.getContent(), responseFormat);
95 | }
96 |
97 | @Override
98 | public void countEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException, ODataLibraryException {
99 |
100 | // 1st we have retrieve the requested EntitySet from the uriInfo object (representation of the parsed service URI)
101 | EdmEntitySet edmEntitySet = getUriResourceEdmEntitySet(uriInfo);
102 |
103 | // 2nd: fetch the data from backend for this requested EntitySetName
104 | // it has to be delivered as EntitySet object
105 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
106 |
107 | EntityCollection entitySet = dataService.getEntityDataList(meta);
108 |
109 | // 3. serialize
110 | Integer entityCount = entitySet.getCount();
111 | if (entityCount != null) {
112 | String valueStr = String.valueOf(entityCount);
113 | ByteArrayInputStream serializerContent = new ByteArrayInputStream(
114 | valueStr.getBytes(Charset.forName("UTF-8")));
115 |
116 | // configure the response object
117 | setResponseContentAndOkStatus(response, serializerContent, ContentType.TEXT_PLAIN);
118 | } else {
119 | // in case there's no value for the property, we can skip the serialization
120 | setResponseNoContentStatus(response);
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/edm/meta/EntityMetaDataBuilder.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.edm.meta;
2 |
3 | import com.bloggingit.odata.edm.annotation.EdmComplexType;
4 | import com.bloggingit.odata.edm.annotation.EdmEntitySet;
5 | import com.bloggingit.odata.edm.annotation.EdmEntityType;
6 | import com.bloggingit.odata.edm.annotation.EdmKey;
7 | import com.bloggingit.odata.edm.annotation.EdmProperty;
8 | import com.bloggingit.odata.edm.enumeration.EdmValueType;
9 | import com.bloggingit.odata.olingo.v4.util.ReflectionUtils;
10 | import java.lang.annotation.Annotation;
11 | import java.lang.reflect.Field;
12 | import java.util.ArrayList;
13 | import java.util.HashSet;
14 | import java.util.List;
15 | import java.util.Set;
16 | import java.util.stream.Stream;
17 | import org.apache.commons.lang3.StringUtils;
18 | import org.apache.commons.lang3.reflect.FieldUtils;
19 |
20 | /**
21 | *
22 | * @author mes
23 | */
24 | public class EntityMetaDataBuilder {
25 |
26 | private static final Class extends Annotation> EDM_ENTITY_SET = EdmEntitySet.class;
27 | private static final Class extends Annotation> EDM_ENTITY_TYPE = EdmEntityType.class;
28 | private static final Class extends Annotation> EDM_COMPLEX_TYPE = EdmComplexType.class;
29 | private static final Class extends Annotation> EDM_PROPERTY = EdmProperty.class;
30 | private static final Class extends Annotation> EDM_KEY = EdmKey.class;
31 |
32 | public static EntityMetaDataContainer createContainer(String serviceNamespace, String edmContainer, String dtoPackage) {
33 |
34 | Set> metaEntities = createEntityMetaDataList(dtoPackage, serviceNamespace);
35 |
36 | return new EntityMetaDataContainer(serviceNamespace, edmContainer, metaEntities);
37 | }
38 |
39 | private static Set> createEntityMetaDataList(String dtoPackage, String serviceNamespace) {
40 | Set> metaEntities = new HashSet<>();
41 |
42 | Set> odataEntityClasses = ReflectionUtils.findClassesInPackageAnnotatedWith(dtoPackage, EDM_ENTITY_TYPE);
43 | Set> odataComplexClasses = ReflectionUtils.findClassesInPackageAnnotatedWith(dtoPackage, EDM_COMPLEX_TYPE);
44 |
45 | Stream> odataAllClasses = Stream.concat(odataEntityClasses.stream(), odataComplexClasses.stream());
46 |
47 | odataAllClasses.map((odataClass) -> {
48 | return createEntityMetaDataFromClass(odataClass, serviceNamespace);
49 | }).forEachOrdered((entity) -> {
50 | metaEntities.add(entity);
51 | });
52 |
53 | return metaEntities;
54 | }
55 |
56 | private static EntityMetaData createEntityMetaDataFromClass(Class entityClass, String serviceNamespace) {
57 | boolean isEntitySet = entityClass.isAnnotationPresent(EDM_ENTITY_SET);
58 | boolean isComplexType = entityClass.isAnnotationPresent(EDM_COMPLEX_TYPE);
59 | List properties = createEntityPropertiesFromClass(entityClass, serviceNamespace);
60 | return new EntityMetaData<>(entityClass, serviceNamespace, isEntitySet, isComplexType, properties);
61 | }
62 |
63 |
64 | private static List createEntityPropertiesFromClass(Class> entityClass, String serviceNamespace) {
65 | List newProperties = new ArrayList<>();
66 |
67 | Field[] fields = FieldUtils.getFieldsWithAnnotation(entityClass, EDM_PROPERTY);
68 |
69 | for (Field field : fields) {
70 |
71 | EdmValueType edmValueType = EdmValueType.valueOf(field);
72 |
73 | Class> fieldTypeClass = field.getType();
74 |
75 | EdmProperty edmProperty = (EdmProperty) field.getAnnotation(EDM_PROPERTY);
76 |
77 | String edmName = (StringUtils.isNotBlank(edmProperty.name())) ? edmProperty.name() : field.getName();
78 |
79 | EntityMetaProperty propertyData = null;
80 |
81 | boolean isKey = (field.isAnnotationPresent(EDM_KEY));
82 |
83 | if (null != edmValueType)
84 | switch (edmValueType) {
85 | case PRIMITIVE:
86 | propertyData = EntityMetaPropertyPrimitve
87 | .builder()
88 | .isKey(isKey)
89 | .name(edmName)
90 | .fieldName(field.getName())
91 | .fieldType(fieldTypeClass)
92 | .maxLength(edmProperty.facets().maxLength())
93 | .nullable(edmProperty.facets().nullable())
94 | .precision(edmProperty.facets().precision())
95 | .scale(edmProperty.facets().scale())
96 | .build();
97 | break;
98 | case ENUM:
99 | propertyData = EntityMetaPropertyEnum
100 | .builder()
101 | .isKey(isKey)
102 | .name(edmName)
103 | .fieldName(field.getName())
104 | .fieldType(fieldTypeClass)
105 | .build();
106 | break;
107 | case COMPLEX:
108 | EntityMetaData> valueComplexMetaData = createEntityMetaDataFromClass(fieldTypeClass, serviceNamespace);
109 | propertyData = EntityMetaPropertyComplex
110 | .builder()
111 | .name(edmName)
112 | .fieldName(field.getName())
113 | .fieldType(fieldTypeClass)
114 | .valueMetaData(valueComplexMetaData)
115 | .build();
116 | break;
117 | case ENTITY:
118 | EntityMetaData> valueEntityMetaData = createEntityMetaDataFromClass(fieldTypeClass, serviceNamespace);
119 | propertyData = EntityMetaPropertyEntity
120 | .builder()
121 | .name(edmName)
122 | .fieldName(field.getName())
123 | .fieldType(fieldTypeClass)
124 | .valueMetaData(valueEntityMetaData)
125 | .build();
126 | break;
127 | default:
128 | break;
129 | }
130 |
131 | newProperties.add(propertyData);
132 | }
133 |
134 | return newProperties;
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/provider/AnnotationEdmProvider.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.provider;
2 |
3 | import com.bloggingit.odata.olingo.v4.mapper.OlingoObjectMapper;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
5 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaDataContainer;
6 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaDataBuilder;
7 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaProperty;
8 | import com.bloggingit.odata.olingo.v4.factory.OlingoObjectFactory;
9 | import com.bloggingit.odata.olingo.v4.mapper.FQNMapper;
10 | import java.util.ArrayList;
11 | import java.util.Arrays;
12 | import java.util.List;
13 | import lombok.Getter;
14 |
15 | import org.apache.olingo.commons.api.edm.FullQualifiedName;
16 | import org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider;
17 | import org.apache.olingo.commons.api.edm.provider.CsdlComplexType;
18 | import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer;
19 | import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainerInfo;
20 | import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet;
21 | import org.apache.olingo.commons.api.edm.provider.CsdlEntityType;
22 | import org.apache.olingo.commons.api.edm.provider.CsdlEnumType;
23 | import org.apache.olingo.commons.api.edm.provider.CsdlSchema;
24 | import org.apache.olingo.commons.api.edm.provider.CsdlStructuralType;
25 | import org.apache.olingo.commons.api.ex.ODataException;
26 |
27 | /**
28 | * Provider-implementation of a {@link CsdlAbstractEdmProvider} for annotation
29 | * support in the entity data model.
30 | */
31 | public class AnnotationEdmProvider extends CsdlAbstractEdmProvider {
32 |
33 | @Getter
34 | private final EntityMetaDataContainer entityMetaDataContainer;
35 |
36 | public AnnotationEdmProvider(String serviceNamespace, String edmContainer, String dtoPackage) {
37 | this.entityMetaDataContainer = EntityMetaDataBuilder.createContainer(serviceNamespace, edmContainer, dtoPackage);
38 | }
39 |
40 | @Override
41 | public List getSchemas() {
42 | List entityTypes = new ArrayList<>();
43 | List enumTypes = new ArrayList<>();
44 | List complexTypes = new ArrayList<>();
45 |
46 | String serviceNamespace = this.entityMetaDataContainer.getServiceNamespace();
47 |
48 | this.entityMetaDataContainer.getAllEntityMetaData().forEach((meta) -> {
49 | CsdlStructuralType structuralType = OlingoObjectMapper.mapEntityMetaDataToCsdlStructuralType(meta, serviceNamespace);
50 |
51 | if (structuralType instanceof CsdlComplexType) {
52 | complexTypes.add((CsdlComplexType) structuralType);
53 | } else {
54 | entityTypes.add((CsdlEntityType) structuralType);
55 | }
56 |
57 | enumTypes.addAll(OlingoObjectMapper.mapEntityMetaDataToCsdlEnumTypeList(meta));
58 | });
59 |
60 | CsdlSchema schema = OlingoObjectFactory
61 | .createCsdlSchema(serviceNamespace, getEntityContainer(), entityTypes, enumTypes, complexTypes);
62 |
63 | return Arrays.asList(schema);
64 | }
65 |
66 | @Override
67 | @SuppressWarnings("unchecked")
68 | public CsdlEnumType getEnumType(FullQualifiedName enumTypeNameFQ) throws ODataException {
69 | CsdlEnumType csdlEnumType = null;
70 | if (enumTypeNameFQ != null
71 | && this.entityMetaDataContainer.getServiceNamespace().equals(enumTypeNameFQ.getNamespace())) {
72 |
73 | EntityMetaProperty propertyData = this.entityMetaDataContainer.getEntityMetaPropertyDataByTypeName(enumTypeNameFQ.getNamespace(), enumTypeNameFQ.getName());
74 |
75 | if (propertyData != null) {
76 | Class enumFieldType = (Class) propertyData.getFieldType();
77 | csdlEnumType = OlingoObjectFactory.createEnumType(enumFieldType);
78 | }
79 | }
80 |
81 | return csdlEnumType;
82 | }
83 |
84 | @Override
85 | public CsdlEntityType getEntityType(FullQualifiedName entityTypeNameFQ) {
86 | EntityMetaData> metaData = this.entityMetaDataContainer.getEntityMetaDataByTypeName(entityTypeNameFQ.getNamespace(), entityTypeNameFQ.getName());
87 |
88 | return (CsdlEntityType) OlingoObjectMapper.mapEntityMetaDataToCsdlStructuralType(metaData, this.entityMetaDataContainer.getServiceNamespace());
89 | }
90 |
91 | @Override
92 | public CsdlEntitySet getEntitySet(FullQualifiedName entityContainerFQ, String entitySetName) {
93 | CsdlEntitySet entitySet = null;
94 |
95 | if (entityContainerFQ != null
96 | && this.entityMetaDataContainer.getServiceNamespace().equals(entityContainerFQ.getNamespace())
97 | && this.entityMetaDataContainer.getEdmContainerName().equals(entityContainerFQ.getName())) {
98 |
99 | EntityMetaData> meta = this.entityMetaDataContainer.getEntityMetaDataByTypeSetName(entitySetName);
100 | if (meta != null) {
101 | entitySet = OlingoObjectFactory.createCsdlEntitySet(meta, entityContainerFQ.getNamespace());
102 | }
103 | }
104 |
105 | return entitySet;
106 | }
107 |
108 | @Override
109 | public CsdlComplexType getComplexType(FullQualifiedName complexTypeNameFQ) throws ODataException {
110 | EntityMetaData> metaData = this.entityMetaDataContainer.getEntityMetaDataByTypeName(complexTypeNameFQ.getNamespace(), complexTypeNameFQ.getName());
111 |
112 | if (metaData.isComplexType()) {
113 | return (CsdlComplexType) OlingoObjectMapper.mapEntityMetaDataToCsdlStructuralType(metaData, this.entityMetaDataContainer.getServiceNamespace());
114 | } else {
115 | return null;
116 | }
117 | }
118 |
119 |
120 | @Override
121 | public CsdlEntityContainer getEntityContainer() {
122 | List entitySets = new ArrayList<>();
123 |
124 | this.entityMetaDataContainer.getAllEntityMetaData().forEach((metaData) -> {
125 | FullQualifiedName containerNameFQ = FQNMapper.createFullQualifiedName(this.entityMetaDataContainer);
126 |
127 | CsdlEntitySet entitySet = getEntitySet(containerNameFQ, metaData.getEntityTypeSetName());
128 | if (entitySet != null) {
129 | entitySets.add(entitySet);
130 | }
131 | });
132 |
133 | return OlingoObjectFactory.createCsdlEntityContainer(this.entityMetaDataContainer.getEdmContainerName(), entitySets);
134 | }
135 |
136 | @Override
137 | public CsdlEntityContainerInfo getEntityContainerInfo(FullQualifiedName entityContainerFQ) {
138 | String serviceNamespace;
139 | String edmContainerName;
140 |
141 | if (entityContainerFQ == null) {
142 | serviceNamespace = this.entityMetaDataContainer.getServiceNamespace();
143 | edmContainerName = this.entityMetaDataContainer.getEdmContainerName();
144 | } else {
145 | serviceNamespace = entityContainerFQ.getNamespace();
146 | edmContainerName = entityContainerFQ.getName();
147 | }
148 |
149 | return OlingoObjectFactory.createCsdlEntityContainerInfo(serviceNamespace, edmContainerName);
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/storage/InMemoryDataStorage.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.storage;
2 |
3 | import com.bloggingit.odata.model.BaseEntity;
4 | import com.bloggingit.odata.model.Book;
5 | import com.bloggingit.odata.exception.EntityDataException;
6 | import com.bloggingit.odata.model.Author;
7 | import com.bloggingit.odata.model.ContactInfo;
8 | import com.bloggingit.odata.model.Gender;
9 | import com.bloggingit.odata.olingo.v4.util.ReflectionUtils;
10 | import java.time.LocalDateTime;
11 | import java.time.Month;
12 | import java.time.ZoneId;
13 | import java.util.ArrayList;
14 | import java.util.Date;
15 | import java.util.List;
16 | import java.util.Map;
17 | import java.util.concurrent.ConcurrentHashMap;
18 | import java.util.concurrent.ConcurrentMap;
19 |
20 | /**
21 | * This class provides a simple in-memory data storage with some example data.
22 | */
23 | public class InMemoryDataStorage {
24 |
25 | private static final ConcurrentMap DATA_BOOKS = new ConcurrentHashMap<>();
26 | private static final ConcurrentMap DATA_AUTHOR = new ConcurrentHashMap<>();
27 |
28 | static {
29 | createAuthorList();
30 | createBookList();
31 | }
32 | private static void createBookList() {
33 | LocalDateTime lDate1 = LocalDateTime.of(2011, Month.JULY, 21, 0, 0);
34 | LocalDateTime lDate2 = LocalDateTime.of(2015, Month.AUGUST, 6, 13, 15);
35 | LocalDateTime lDate3 = LocalDateTime.of(2013, Month.MAY, 12, 0, 0);
36 |
37 | Date date1 = Date.from(lDate1.atZone(ZoneId.systemDefault()).toInstant());
38 | Date date2 = Date.from(lDate2.atZone(ZoneId.systemDefault()).toInstant());
39 | Date date3 = Date.from(lDate3.atZone(ZoneId.systemDefault()).toInstant());
40 |
41 | Book book1 = new Book("Book Title 1", "This is the description of book 1", date1, DATA_AUTHOR.get(1L), 9.95, true);
42 | Book book2 = new Book("Book Title 2", "This is the description of book 2", date2, DATA_AUTHOR.get(2L), 5.99, true);
43 | Book book3 = new Book("Book Title 3", "This is the description of book 3", date3, DATA_AUTHOR.get(3L), 14.50, false);
44 |
45 | book1.setId(1L);
46 | book2.setId(2L);
47 | book3.setId(3L);
48 |
49 | DATA_BOOKS.put(book1.getId(), book1);
50 | DATA_BOOKS.put(book2.getId(), book2);
51 | DATA_BOOKS.put(book3.getId(), book3);
52 | }
53 |
54 | private static void createAuthorList() {
55 | Author author1 = new Author("Author 1", Gender.MALE, new ContactInfo("author1@test.xyz", "123/456"));
56 | Author author2 = new Author("Author 2", Gender.FEMALE, new ContactInfo("author2@test.xyz", "654/321"));
57 | Author author3 = new Author("Author 3", Gender.UNKOWN, new ContactInfo("author3@test.xyz", null));
58 |
59 | author1.setId(1L);
60 | author2.setId(2L);
61 | author3.setId(3L);
62 |
63 | DATA_AUTHOR.put(author1.getId(), author1);
64 | DATA_AUTHOR.put(author2.getId(), author2);
65 | DATA_AUTHOR.put(author3.getId(), author3);
66 | }
67 |
68 | @SuppressWarnings("unchecked")
69 | private static ConcurrentMap getDataMapByEntityClass(Class entityClazz) {
70 | ConcurrentMap entities = null;
71 |
72 | if (Book.class.equals(entityClazz)) {
73 | entities = (ConcurrentMap) DATA_BOOKS;
74 | } else if (Author.class.equals(entityClazz)) {
75 | entities = (ConcurrentMap) DATA_AUTHOR;
76 | }
77 |
78 | return entities;
79 | }
80 |
81 | public static List getDataListByBaseEntityClass(Class entityClazz) {
82 | final ConcurrentMap entityMap = getDataMapByEntityClass(entityClazz);
83 |
84 | return new ArrayList<>(entityMap.values());
85 | }
86 |
87 | public static T getDataByClassAndId(Class entityClazz, long id) {
88 | final ConcurrentMap entityMap = getDataMapByEntityClass(entityClazz);
89 |
90 | return entityMap.get(id);
91 | }
92 |
93 | public static void deleteDataByClassAndId(Class entityClazz, long id) {
94 | final ConcurrentMap entityMap = getDataMapByEntityClass(entityClazz);
95 | entityMap.remove(id);
96 | }
97 |
98 | public static T createEntity(T newEntity) throws EntityDataException {
99 |
100 | if (newEntity == null) {
101 | throw new EntityDataException("Unable to create entity, because no entity given");
102 | }
103 |
104 | if (newEntity instanceof BaseEntity) {
105 | if (newEntity instanceof Book) {
106 | Author author = ((Book) newEntity).getAuthor();
107 | if (author != null) {
108 | if (author.getId() > 0) {
109 | author = (Author) getDataByClassAndId(newEntity.getClass(), author.getId());
110 | } else {
111 | author = createEntity(author);
112 | }
113 | ((Book) newEntity).setAuthor(author);
114 | }
115 | }
116 |
117 | @SuppressWarnings("unchecked")
118 | final ConcurrentMap entityMap = getDataMapByEntityClass((Class) newEntity.getClass());
119 |
120 | BaseEntity baseEntity = (BaseEntity) newEntity;
121 |
122 | if (baseEntity.getId() == 0 || entityMap.putIfAbsent(baseEntity.getId(), newEntity) == null) {
123 | baseEntity.setId(entityMap.size() + 1);
124 | entityMap.put(baseEntity.getId(), newEntity);
125 | } else {
126 | throw new EntityDataException("Could not create entity, because it already exists");
127 | }
128 | } else {
129 | throw new EntityDataException("Unable to create unsupported entity class" + newEntity);
130 | }
131 |
132 | return newEntity;
133 | }
134 |
135 | @SuppressWarnings("unchecked")
136 | public static T updateEntity(Class entityClazz, long id, Map newPropertyValues, boolean nullableUnkownProperties) throws EntityDataException {
137 | T updatedEntity = null;
138 |
139 | if (BaseEntity.class.isAssignableFrom(entityClazz)) {
140 | final ConcurrentMap entityMap = (ConcurrentMap) getDataMapByEntityClass(entityClazz);
141 |
142 | BaseEntity baseEntity = entityMap.get(id);
143 |
144 | if (baseEntity == null) {
145 | throw new EntityDataException("Unable to update entity, because the entity does not exist");
146 | }
147 |
148 | newPropertyValues.entrySet().forEach((propEntry) -> {
149 | String fieldname = propEntry.getKey();
150 | Object value = propEntry.getValue();
151 | if (!("id".equalsIgnoreCase(fieldname))) {
152 | ReflectionUtils.invokePropertySetter(fieldname, baseEntity, value);
153 | }
154 | });
155 |
156 | entityMap.put(baseEntity.getId(), baseEntity);
157 |
158 | updatedEntity = (T) baseEntity;
159 | } else {
160 | throw new EntityDataException("Unable to update unsupported entity class" + entityClazz);
161 | }
162 |
163 | return updatedEntity;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 |
8 | com.bloggingit
9 | odatav4-javaee-example-apache-olingo
10 | 1.0.0-SNAPSHOT
11 |
12 | war
13 |
14 | ODataV4-JavaEE-Example-Apache-Olingo
15 |
16 |
17 | ${project.build.directory}/endorsed
18 |
19 | UTF-8
20 | 1.8
21 | 1.8
22 |
23 | ${project.basedir}
24 |
25 | 4.12
26 | 2.10.4
27 | 3.6.1
28 | 3.0.2
29 | 3.0.0
30 |
31 | 7.0
32 | 4.3.0
33 |
34 |
35 |
36 |
37 |
38 | javax
39 | javaee-api
40 | ${version.javaee.api}
41 | provided
42 |
43 |
44 |
45 |
46 | org.projectlombok
47 | lombok
48 | 1.16.16
49 |
50 |
51 |
52 |
53 |
54 | org.apache.commons
55 | commons-lang3
56 | 3.5
57 |
58 |
59 |
60 | commons-io
61 | commons-io
62 | 2.5
63 |
64 |
65 |
66 |
67 |
68 | org.apache.olingo
69 | odata-server-api
70 | ${odata.version}
71 |
72 |
73 | org.apache.olingo
74 | odata-server-core
75 | ${odata.version}
76 |
77 |
78 |
79 | org.apache.olingo
80 | odata-commons-api
81 | ${odata.version}
82 |
83 |
84 | org.apache.olingo
85 | odata-commons-core
86 | ${odata.version}
87 |
88 |
89 |
90 |
91 | org.reflections
92 | reflections
93 | 0.9.11
94 |
95 |
96 |
97 |
98 |
99 | junit
100 | junit
101 | ${version.test.junit}
102 | test
103 |
104 |
105 |
106 |
107 |
108 | ${project.artifactId}
109 |
110 |
111 |
112 |
113 | org.apache.maven.plugins
114 | maven-resources-plugin
115 | ${version.maven.resources-plugin}
116 |
117 |
118 |
119 | org.apache.maven.plugins
120 | maven-compiler-plugin
121 | ${version.maven.compiler-plugin}
122 |
123 | true
124 | true
125 | true
126 |
127 | ${endorsed.dir}
128 |
129 |
130 |
131 |
132 |
133 | org.apache.maven.plugins
134 | maven-war-plugin
135 | ${version.maven.war-plugin}
136 |
137 | false
138 |
139 | true
140 |
141 |
142 |
143 |
144 | ${project.name}
145 | ${project.version}
146 |
147 |
148 | true
149 | classes
150 |
151 |
152 |
153 |
154 |
155 | org.apache.maven.plugins
156 | maven-javadoc-plugin
157 | ${version.reporting.javadoc}
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | org.apache.maven.plugins
171 | maven-javadoc-plugin
172 | ${version.reporting.javadoc}
173 |
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/processor/DataPrimitiveValueProcessor.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.processor;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaDataContainer;
5 | import com.bloggingit.odata.olingo.v4.service.OlingoDataService;
6 | import java.io.ByteArrayInputStream;
7 | import java.io.InputStream;
8 | import java.nio.charset.Charset;
9 | import java.util.List;
10 | import java.util.Locale;
11 | import org.apache.olingo.commons.api.data.ContextURL;
12 | import org.apache.olingo.commons.api.data.Entity;
13 | import org.apache.olingo.commons.api.data.Property;
14 | import org.apache.olingo.commons.api.edm.EdmEntitySet;
15 | import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
16 | import org.apache.olingo.commons.api.edm.EdmProperty;
17 | import org.apache.olingo.commons.api.format.ContentType;
18 | import org.apache.olingo.commons.api.http.HttpStatusCode;
19 | import org.apache.olingo.server.api.OData;
20 | import org.apache.olingo.server.api.ODataApplicationException;
21 | import org.apache.olingo.server.api.ODataLibraryException;
22 | import org.apache.olingo.server.api.ODataRequest;
23 | import org.apache.olingo.server.api.ODataResponse;
24 | import org.apache.olingo.server.api.ServiceMetadata;
25 | import org.apache.olingo.server.api.processor.PrimitiveValueProcessor;
26 | import org.apache.olingo.server.api.serializer.ODataSerializer;
27 | import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
28 | import org.apache.olingo.server.api.serializer.SerializerResult;
29 | import org.apache.olingo.server.api.uri.UriInfo;
30 | import org.apache.olingo.server.api.uri.UriParameter;
31 | import org.apache.olingo.server.api.uri.UriResourceProperty;
32 |
33 | /**
34 | * This class is invoked by the Apache Olingo framework when the the OData
35 | * service is invoked order to display primitive property data of a entity.
36 | */
37 | public class DataPrimitiveValueProcessor extends AbstractEntityMetaDataProcessor implements PrimitiveValueProcessor {
38 |
39 | private OData odata;
40 | private ServiceMetadata serviceMetadata;
41 | private final EntityMetaDataContainer entityMetaDataCollection;
42 |
43 | private final OlingoDataService dataService;
44 |
45 | public DataPrimitiveValueProcessor(OlingoDataService dataService, EntityMetaDataContainer entityMetaDataCollection) {
46 | this.dataService = dataService;
47 | this.entityMetaDataCollection = entityMetaDataCollection;
48 | }
49 |
50 | @Override
51 | public void init(OData odata, ServiceMetadata serviceMetadata) {
52 | this.odata = odata;
53 | this.serviceMetadata = serviceMetadata;
54 | }
55 |
56 | @Override
57 | public void readPrimitiveValue(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
58 | // 1. Retrieve info from URI
59 | // 1.1. retrieve the info about the requested entity set
60 | EdmEntitySet edmEntitySet = getUriResourceEdmEntitySet(uriInfo);
61 | // the key for the entity
62 | List keyPredicates = getUriResourceKeyPredicates(uriInfo);
63 |
64 | // 1.2. retrieve the requested (Edm) property
65 | // Note: only in our example we can rely that the second segment is the is the Property
66 | UriResourceProperty uriProperty = (UriResourceProperty) uriInfo.getUriResourceParts().get(1);
67 | EdmProperty edmProperty = uriProperty.getProperty();
68 | String edmPropertyName = edmProperty.getName();
69 |
70 | // 2. retrieve data from backend
71 | // 2.1. retrieve the entity data, for which the property has to be read
72 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
73 |
74 | Entity entity = this.dataService.getEntityData(meta, keyPredicates);
75 |
76 | if (entity == null) { // Bad request
77 | throw new ODataApplicationException("Entity not found",
78 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
79 | }
80 |
81 | // 2.2. retrieve the property data from the entity
82 | Property property = entity.getProperty(edmPropertyName);
83 | if (property == null) {
84 | throw new ODataApplicationException("Property not found",
85 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
86 | }
87 |
88 | // 3. serialize
89 | Object value = property.getValue();
90 | if (value != null) {
91 | String valueStr = String.valueOf(value);
92 | ByteArrayInputStream serializerContent = new ByteArrayInputStream(
93 | valueStr.getBytes(Charset.forName("UTF-8")));
94 |
95 | // configure the response object
96 | setResponseContentAndOkStatus(response, serializerContent, ContentType.TEXT_PLAIN);
97 | } else {
98 | // in case there's no value for the property, we can skip the serialization
99 | setResponseNoContentStatus(response);
100 | }
101 | }
102 |
103 | @Override
104 | public void updatePrimitiveValue(ODataRequest odr, ODataResponse odr1, UriInfo ui, ContentType ct, ContentType ct1) throws ODataApplicationException, ODataLibraryException {
105 | throw new UnsupportedOperationException("Not supported yet.");
106 | }
107 |
108 | @Override
109 | public void deletePrimitiveValue(ODataRequest odr, ODataResponse odr1, UriInfo ui) throws ODataApplicationException, ODataLibraryException {
110 | throw new UnsupportedOperationException("Not supported yet.");
111 | }
112 |
113 | @Override
114 | public void readPrimitive(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
115 | // 1. Retrieve info from URI
116 | // 1.1. retrieve the info about the requested entity set
117 | EdmEntitySet edmEntitySet = getUriResourceEdmEntitySet(uriInfo);
118 | // the key for the entity
119 | List keyPredicates = getUriResourceKeyPredicates(uriInfo);
120 |
121 | // 1.2. retrieve the requested (Edm) property
122 | EdmProperty edmProperty = getUriResourceEdmProperty(uriInfo);
123 | String edmPropertyName = edmProperty.getName();
124 | // in our example, we know we have only primitive types in our model
125 | EdmPrimitiveType edmPropertyType = (EdmPrimitiveType) edmProperty.getType();
126 |
127 | // 2. retrieve data from backend
128 | // 2.1. retrieve the entity data, for which the property has to be read
129 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
130 |
131 | Entity entity = this.dataService.getEntityData(meta, keyPredicates);
132 |
133 | if (entity == null) { // Bad request
134 | throw new ODataApplicationException("Entity not found",
135 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
136 | }
137 |
138 | // 2.2. retrieve the property data from the entity
139 | Property property = entity.getProperty(edmPropertyName);
140 | if (property == null) {
141 | throw new ODataApplicationException("Property not found",
142 | HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
143 | }
144 |
145 | // 3. serialize
146 | Object value = property.getValue();
147 | if (value != null) {
148 | // 3.1. configure the serializer
149 | ODataSerializer serializer = odata.createSerializer(responseFormat);
150 |
151 | ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).navOrPropertyPath(edmPropertyName).build();
152 | PrimitiveSerializerOptions options = PrimitiveSerializerOptions.with().contextURL(contextUrl).build();
153 | // 3.2. serialize
154 | SerializerResult serializerResult = serializer.primitive(serviceMetadata, edmPropertyType, property, options);
155 | InputStream propertyStream = serializerResult.getContent();
156 |
157 | //4. configure the response object
158 | setResponseContentAndOkStatus(response, propertyStream, responseFormat);
159 | } else {
160 | // in case there's no value for the property, we can skip the serialization
161 | setResponseNoContentStatus(response);
162 | }
163 | }
164 |
165 | @Override
166 | public void updatePrimitive(ODataRequest odr, ODataResponse odr1, UriInfo ui, ContentType ct, ContentType ct1) throws ODataApplicationException, ODataLibraryException {
167 | throw new UnsupportedOperationException("Not supported yet.");
168 | }
169 |
170 | @Override
171 | public void deletePrimitive(ODataRequest odr, ODataResponse odr1, UriInfo ui) throws ODataApplicationException, ODataLibraryException {
172 | throw new UnsupportedOperationException("Not supported yet.");
173 | }
174 |
175 | }
176 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/factory/OlingoObjectFactory.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.factory;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaProperty;
5 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyEntity;
6 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyPrimitve;
7 | import com.bloggingit.odata.olingo.v4.mapper.FQNMapper;
8 | import java.net.URI;
9 | import java.net.URISyntaxException;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import org.apache.commons.lang3.EnumUtils;
13 | import org.apache.olingo.commons.api.Constants;
14 | import org.apache.olingo.commons.api.data.ComplexValue;
15 | import org.apache.olingo.commons.api.data.Entity;
16 | import org.apache.olingo.commons.api.data.Link;
17 | import org.apache.olingo.commons.api.data.Property;
18 | import org.apache.olingo.commons.api.data.ValueType;
19 | import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
20 | import org.apache.olingo.commons.api.edm.FullQualifiedName;
21 | import org.apache.olingo.commons.api.edm.provider.CsdlComplexType;
22 | import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer;
23 | import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainerInfo;
24 | import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet;
25 | import org.apache.olingo.commons.api.edm.provider.CsdlEntityType;
26 | import org.apache.olingo.commons.api.edm.provider.CsdlEnumMember;
27 | import org.apache.olingo.commons.api.edm.provider.CsdlEnumType;
28 | import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty;
29 | import org.apache.olingo.commons.api.edm.provider.CsdlNavigationPropertyBinding;
30 | import org.apache.olingo.commons.api.edm.provider.CsdlProperty;
31 | import org.apache.olingo.commons.api.edm.provider.CsdlPropertyRef;
32 | import org.apache.olingo.commons.api.edm.provider.CsdlSchema;
33 | import org.apache.olingo.commons.api.ex.ODataRuntimeException;
34 |
35 | /**
36 | *
37 | * @author mes
38 | */
39 | public class OlingoObjectFactory {
40 |
41 | public static CsdlEntitySet createCsdlEntitySet(EntityMetaData meta, String serviceNamespace) {
42 | CsdlEntitySet csdlEntitySet = new CsdlEntitySet();
43 |
44 | csdlEntitySet.setName(meta.getEntityTypeSetName());
45 |
46 | csdlEntitySet.setType(FQNMapper.createFullQualifiedName(serviceNamespace, meta));
47 |
48 | List navPropBindingList = new ArrayList<>();
49 | for (EntityMetaProperty metaProp : meta.getProperties()) {
50 | if (metaProp instanceof EntityMetaPropertyEntity) { // add navigation ref only
51 | EntityMetaPropertyEntity metaPropEntity = (EntityMetaPropertyEntity) metaProp;
52 | navPropBindingList.add(createCsdlNavigationPropertyBinding(metaPropEntity.getEdmName(), metaPropEntity.getValueMetaData().getEntityTypeSetName()));
53 | }
54 |
55 | }
56 | csdlEntitySet.setNavigationPropertyBindings(navPropBindingList);
57 |
58 |
59 | return csdlEntitySet;
60 | }
61 |
62 | public static CsdlEntityContainer createCsdlEntityContainer(String containerName, List entitySets) {
63 | CsdlEntityContainer entityContainer = new CsdlEntityContainer();
64 |
65 | entityContainer.setName(containerName);
66 |
67 | entityContainer.setEntitySets(entitySets);
68 |
69 | return entityContainer;
70 | }
71 |
72 | public static CsdlEntityContainerInfo createCsdlEntityContainerInfo(String serviceNamespace, String containerName) {
73 | CsdlEntityContainerInfo csdlEntityContainerInfo = new CsdlEntityContainerInfo();
74 |
75 | FullQualifiedName containerNameFQ = FQNMapper.createFullQualifiedName(serviceNamespace, containerName);
76 |
77 | csdlEntityContainerInfo.setContainerName(containerNameFQ);
78 |
79 | return csdlEntityContainerInfo;
80 | }
81 |
82 | public static CsdlSchema createCsdlSchema(String serviceNamespace, CsdlEntityContainer entityContainer, List entityTypes, List enumTypes, List complexTypes) {
83 | CsdlSchema csdlSchema = new CsdlSchema();
84 |
85 | csdlSchema.setNamespace(serviceNamespace);
86 | csdlSchema.setEntityContainer(entityContainer);
87 | csdlSchema.setEntityTypes(entityTypes);
88 | csdlSchema.setEnumTypes(enumTypes);
89 | csdlSchema.setComplexTypes(complexTypes);
90 |
91 | return csdlSchema;
92 | }
93 |
94 | public static CsdlProperty createCsdlProperty(EntityMetaProperty metaProp, FullQualifiedName typeFQN, String defaultVal) {
95 | CsdlProperty csdlProperty = new CsdlProperty();
96 |
97 | csdlProperty.setName(metaProp.getEdmName()).setType(typeFQN);
98 |
99 | if (metaProp instanceof EntityMetaPropertyPrimitve) {
100 |
101 | EntityMetaPropertyPrimitve primitiveProp = (EntityMetaPropertyPrimitve) metaProp;
102 |
103 | csdlProperty
104 | .setNullable(metaProp.getNullable());
105 |
106 | if (primitiveProp.getMaxLength() != -1) {
107 | csdlProperty.setMaxLength(primitiveProp.getMaxLength());
108 | }
109 | if (primitiveProp.getPrecision() != -1) {
110 | csdlProperty.setPrecision(primitiveProp.getPrecision());
111 | }
112 | if (primitiveProp.getScale() != -1) {
113 | csdlProperty.setScale(primitiveProp.getScale());
114 | }
115 | if (defaultVal != null) {
116 | csdlProperty.setDefaultValue(defaultVal);
117 | }
118 | }
119 |
120 | return csdlProperty;
121 | }
122 | /**
123 | *
124 | * @param path target entitySet, where the nav prop points to
125 | * @param target target entitySet, where the nav prop points to
126 | * @return a CsdlNavigationPropertyBinding for the entity set
127 | */
128 | public static CsdlNavigationPropertyBinding createCsdlNavigationPropertyBinding(String path, String target) {
129 | CsdlNavigationPropertyBinding csdlNavigationPropertyBinding = new CsdlNavigationPropertyBinding();
130 | csdlNavigationPropertyBinding.setPath(path);
131 | csdlNavigationPropertyBinding.setTarget(target);
132 | return csdlNavigationPropertyBinding;
133 | }
134 |
135 | public static > CsdlEnumType createEnumType(final Class enumClass) {
136 |
137 | List enumList = EnumUtils.getEnumList(enumClass);
138 |
139 | List csdlEnumMember = new ArrayList<>();
140 |
141 | enumList.forEach((entry) -> {
142 | csdlEnumMember.add(new CsdlEnumMember().setName(entry.name()).setValue(String.valueOf(entry.ordinal())));
143 | });
144 |
145 | return new CsdlEnumType()
146 | .setName(enumClass.getSimpleName())
147 | .setMembers(csdlEnumMember)
148 | .setUnderlyingType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName());
149 | }
150 |
151 | public static CsdlEntityType createCsdlEntityType(String entityTypeName, List properties, List csdlNavigationProperty, List keys) {
152 | CsdlEntityType csdlEntityType = new CsdlEntityType();
153 |
154 | csdlEntityType
155 | .setName(entityTypeName)
156 | .setProperties(properties)
157 | .setNavigationProperties(csdlNavigationProperty)
158 | .setKey(keys);
159 |
160 | return csdlEntityType;
161 | }
162 |
163 | public static CsdlComplexType createCsdlComplexType(String entityTypeName, List properties) {
164 | CsdlComplexType csdlComplexType = new CsdlComplexType();
165 |
166 | csdlComplexType
167 | .setName(entityTypeName)
168 | .setProperties(properties);
169 |
170 | return csdlComplexType;
171 | }
172 |
173 | public static CsdlPropertyRef createCsdlPropertyRef(String name) {
174 | CsdlPropertyRef csdlPropertyRef = new CsdlPropertyRef();
175 |
176 | csdlPropertyRef.setName(name);
177 |
178 | return csdlPropertyRef;
179 | }
180 |
181 | // https://olingo.apache.org/doc/odata4/tutorials/navigation/tutorial_navigation.html
182 | /**
183 | *
184 | * @param typeFQ fully qualified name of the entity type to which we’re
185 | * navigating.
186 | * @param name the name of the navigation property is used as segment in the
187 | * URI.
188 | * @param nullable if the navigation target is required. the default is
189 | * assumed to be “true”.
190 | * @param partner An attribute, used to define a bi-directional
191 | * relationship. Specifies a path from the entity type to the navigation
192 | * property. In our example, we can navigate from book to author and from
193 | * author to book
194 | * @return a navigation property
195 | */
196 | public static CsdlNavigationProperty createCsdlNavigationProperty(FullQualifiedName typeFQ, String name, Boolean nullable, String partner) {
197 | boolean isNullable = (nullable == null) ? true : nullable;
198 | return new CsdlNavigationProperty()
199 | .setName(name)
200 | .setType(typeFQ)
201 | .setNullable(isNullable)
202 | .setPartner(partner);
203 | }
204 |
205 |
206 | public static ComplexValue createComplexValue(List properties) {
207 | ComplexValue complexValue = new ComplexValue();
208 | List complexSubValues = complexValue.getValue();
209 | complexSubValues.addAll(properties);
210 |
211 | return complexValue;
212 | }
213 |
214 | public static Property createProperty(String name, ValueType valueType, Object value) {
215 | return new Property(null, name, valueType, value);
216 | }
217 |
218 | public static Entity createEntity(List properties, List navigationLinks, URI key) {
219 | Entity entity = new Entity();
220 | entity.getProperties().addAll(properties);
221 | entity.getNavigationLinks().addAll(navigationLinks);
222 | entity.setId(key);
223 |
224 | return entity;
225 | }
226 |
227 | public static Link createLink(String title, Entity entity) {
228 | Link link = new Link();
229 | link.setTitle(title);
230 | link.setInlineEntity(entity);
231 | link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE);
232 | link.setRel(Constants.NS_ASSOCIATION_LINK_REL + title);
233 | return link;
234 | }
235 |
236 | public static URI createId(String entitySetName, Object id) {
237 | try {
238 | return new URI(entitySetName + "(" + String.valueOf(id) + ")");
239 | } catch (URISyntaxException e) {
240 | throw new ODataRuntimeException("Unable to create id for entity: " + entitySetName, e);
241 | }
242 | }
243 |
244 | public static T createInstance(final Class clazz) {
245 | try {
246 | return clazz.newInstance();
247 | } catch (InstantiationException | IllegalAccessException ex) {
248 | throw new ODataRuntimeException(ex);
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/mapper/OlingoObjectMapper.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.mapper;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaProperty;
5 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyComplex;
6 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaPropertyEntity;
7 | import com.bloggingit.odata.olingo.v4.factory.OlingoObjectFactory;
8 | import com.bloggingit.odata.olingo.v4.util.ReflectionUtils;
9 | import java.net.URI;
10 | import java.util.ArrayList;
11 | import java.util.Calendar;
12 | import java.util.Collection;
13 | import java.util.List;
14 | import org.apache.commons.lang3.EnumUtils;
15 | import org.apache.olingo.commons.api.data.ComplexValue;
16 | import org.apache.olingo.commons.api.data.Entity;
17 | import org.apache.olingo.commons.api.data.EntityCollection;
18 | import org.apache.olingo.commons.api.data.Link;
19 | import org.apache.olingo.commons.api.data.Linked;
20 | import org.apache.olingo.commons.api.data.Property;
21 | import org.apache.olingo.commons.api.data.ValueType;
22 | import org.apache.olingo.commons.api.edm.FullQualifiedName;
23 | import org.apache.olingo.commons.api.edm.provider.CsdlEnumType;
24 | import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty;
25 | import org.apache.olingo.commons.api.edm.provider.CsdlProperty;
26 | import org.apache.olingo.commons.api.edm.provider.CsdlPropertyRef;
27 | import org.apache.olingo.commons.api.edm.provider.CsdlStructuralType;
28 |
29 | /**
30 | * Mapper provides methods to convert data into a certain Apache Olingo data
31 | * structure.
32 | */
33 | public class OlingoObjectMapper {
34 |
35 | public static EntityCollection mapObjectEntitiesToOlingoEntityCollection(Collection objEntities, EntityMetaData entityMetaData) {
36 | EntityCollection entityCollection = new EntityCollection();
37 | entityCollection.setCount(objEntities.size());
38 |
39 | objEntities.forEach((entity) -> {
40 | entityCollection
41 | .getEntities()
42 | .add(mapObjEntityToOlingoEntity(entityMetaData, entity));
43 | });
44 | return entityCollection;
45 | }
46 |
47 | public static Entity mapObjEntityToOlingoEntity(EntityMetaData> entityMetaData, Object objEntity) {
48 |
49 | List metaProperties = entityMetaData.getProperties();
50 |
51 | List properties = new ArrayList<>();
52 | List navigationLinks = new ArrayList<>();
53 |
54 | URI keyId = null;
55 | for (EntityMetaProperty metaProp : metaProperties) {
56 | if (metaProp instanceof EntityMetaPropertyEntity) {
57 | Object metaPropValue = ReflectionUtils.invokePropertyGetter(metaProp.getFieldName(), objEntity);
58 | if (metaPropValue != null) {
59 | EntityMetaData> valueMetaData = ((EntityMetaPropertyEntity) metaProp).getValueMetaData();
60 | Entity subEntity = mapObjEntityToOlingoEntity(valueMetaData, metaPropValue);
61 | navigationLinks.add(OlingoObjectFactory.createLink(metaProp.getEdmName(), subEntity));
62 | }
63 | } else {
64 | Property property = mapMetaPropertyDataToOlingoProperty(objEntity, metaProp);
65 | properties.add(property);
66 |
67 | if (metaProp.isKey()) {
68 | keyId = OlingoObjectFactory.createId(entityMetaData.getEntityTypeSetName(), property.getValue());
69 | }
70 | }
71 | }
72 |
73 | return OlingoObjectFactory.createEntity(properties, navigationLinks, keyId);
74 | }
75 |
76 | public static T mapOlingoEntityToObjectEntity(EntityMetaData entityMetaData, Linked linkedEntity) {
77 |
78 | T objEntity = OlingoObjectFactory.createInstance(entityMetaData.getEntityClass());
79 |
80 | List metaProperties = entityMetaData.getProperties();
81 |
82 | metaProperties.forEach((metaProp) -> {
83 | Object val = null;
84 | if (metaProp instanceof EntityMetaPropertyEntity) {
85 | EntityMetaData> propEntityMetaData = ((EntityMetaPropertyEntity) metaProp).getValueMetaData();
86 | Link navigationLink = linkedEntity.getNavigationLink(metaProp.getEdmName());
87 | Entity propInlineEntity = navigationLink.getInlineEntity();
88 | val = mapOlingoEntityToObjectEntity(propEntityMetaData, propInlineEntity);
89 | } else if (metaProp instanceof EntityMetaPropertyComplex) {
90 | EntityMetaData> propComplexMetaData = ((EntityMetaPropertyComplex) metaProp).getValueMetaData();
91 | Property property = ((Entity) linkedEntity).getProperty(metaProp.getEdmName());
92 | if (property != null) {
93 | val = mapOlingoEntityToObjectEntity(propComplexMetaData, (ComplexValue) property.getValue());
94 | }
95 | } else if (linkedEntity instanceof Entity) {
96 | Property property = ((Entity) linkedEntity).getProperty(metaProp.getEdmName());
97 | val = mapOlingoPropertyToObjValue(property, metaProp);
98 | } else if (linkedEntity instanceof ComplexValue) {
99 | for (Property property : ((ComplexValue) linkedEntity).getValue()) {
100 | if (metaProp.getEdmName().equals(property.getName())) {
101 | val = mapOlingoPropertyToObjValue(property, metaProp);
102 | break;
103 | }
104 | }
105 | }
106 | if (val != null) {
107 | ReflectionUtils.invokePropertySetter(metaProp.getFieldName(), objEntity, val);
108 | }
109 | });
110 |
111 | return objEntity;
112 | }
113 |
114 | @SuppressWarnings("unchecked")
115 | public static > List mapEntityMetaDataToCsdlEnumTypeList(final EntityMetaData meta) {
116 | List enumTypes = new ArrayList<>();
117 |
118 | meta.getEnumPropertyData().forEach((prop) -> {
119 | enumTypes.add(OlingoObjectFactory.createEnumType((Class) prop.getFieldType()));
120 | });
121 |
122 | return enumTypes;
123 | }
124 |
125 | public static CsdlStructuralType mapEntityMetaDataToCsdlStructuralType(EntityMetaData metaData, String serviceNamespace) {
126 | Class objEntityClass = metaData.getEntityClass();
127 |
128 | List properties = new ArrayList<>();
129 | List keys = new ArrayList<>();
130 | List navigations = new ArrayList<>();
131 |
132 | metaData.getProperties().stream().map((metaProp) -> {
133 |
134 | FullQualifiedName typeFQN = FQNMapper.mapToPropertyValueTypeFQN(serviceNamespace, metaProp);
135 | Object objEntity = OlingoObjectFactory.createInstance(objEntityClass);
136 |
137 | if (metaProp instanceof EntityMetaPropertyEntity) { // add navigation ref only
138 | EntityMetaPropertyEntity metaPropEntity = (EntityMetaPropertyEntity) metaProp;
139 | navigations.add(OlingoObjectFactory.createCsdlNavigationProperty(typeFQN, metaPropEntity.getEdmName(), metaPropEntity.getNullable(), null));
140 | } else {
141 | Object val = ReflectionUtils.invokePropertyGetter(metaProp.getFieldName(), objEntity);
142 | properties.add(OlingoObjectFactory.createCsdlProperty(metaProp, typeFQN, String.valueOf(val)));
143 | }
144 |
145 | return metaProp;
146 | }).filter((metaProp) -> (metaProp.isKey())).forEachOrdered((metaProp) -> {
147 | keys.add(OlingoObjectFactory.createCsdlPropertyRef(metaProp.getEdmName()));
148 | });
149 |
150 | CsdlStructuralType structuralType;
151 | if (metaData.isComplexType()) {
152 | structuralType = OlingoObjectFactory.createCsdlComplexType(metaData.getEntityTypeName(), properties);
153 | } else {
154 | structuralType = OlingoObjectFactory.createCsdlEntityType(metaData.getEntityTypeName(), properties, navigations, keys);
155 | }
156 |
157 | return structuralType;
158 | }
159 |
160 | @SuppressWarnings("unchecked")
161 | public static > Object mapOlingoPropertyToObjValue(Property property, EntityMetaProperty metaProp) {
162 |
163 | Class> fieldType = metaProp.getFieldType();
164 |
165 | Object val = null;
166 | if (property != null) {
167 | val = property.getValue();
168 | if (val != null && !fieldType.equals(val.getClass())) {
169 | if (property.isEnum()) {
170 | val = EnumUtils.getEnumList((Class) fieldType).get(Integer.parseInt(val.toString()));
171 | } else if (val instanceof Calendar) {
172 | val = ((Calendar) val).getTime();
173 | }
174 | }
175 | }
176 | return val;
177 | }
178 |
179 | @SuppressWarnings("unchecked")
180 | public static > Property mapMetaPropertyDataToOlingoProperty(T objEntity, EntityMetaProperty metaProp) {
181 | Object val = ReflectionUtils.invokePropertyGetter(metaProp.getFieldName(), objEntity);
182 | ValueType valueType = OlingoTypeMapper.mapToValueType(metaProp);
183 | Object oDataValue = null;
184 | if (val != null) {
185 | if (null == valueType) { //primitive fallback
186 | oDataValue = val;
187 | } else {
188 | switch (valueType) {
189 | case ENUM:
190 | oDataValue = EnumUtils.getEnum((Class) metaProp.getFieldType(), String.valueOf(val)).ordinal();
191 | break;
192 | case COMPLEX:
193 | oDataValue = mapMetaPropertyValueToOlingoLinked(val, metaProp);
194 | break;
195 | case ENTITY:
196 | throw new IllegalArgumentException("Can't create entity property. Use navigation ref instead");
197 | default:
198 | //primitive fallback
199 | oDataValue = val;
200 | break;
201 | }
202 | }
203 | }
204 |
205 | return OlingoObjectFactory.createProperty(metaProp.getEdmName(), valueType, oDataValue);
206 | }
207 |
208 | @SuppressWarnings("unchecked")
209 | public static > Linked mapMetaPropertyValueToOlingoLinked(Object value, EntityMetaProperty metaProp) {
210 | Linked valuable = null;
211 |
212 | if (value != null && metaProp instanceof EntityMetaPropertyComplex) {
213 | EntityMetaPropertyComplex propComplex = (EntityMetaPropertyComplex) metaProp;
214 |
215 | List subProperties = new ArrayList<>();
216 | propComplex.getValueMetaData().getProperties().forEach((subMetaProp) -> {
217 | subProperties.add(mapMetaPropertyDataToOlingoProperty(value, subMetaProp));
218 | });
219 |
220 | valuable = OlingoObjectFactory.createComplexValue(subProperties);
221 | }
222 | return valuable;
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/src/main/java/com/bloggingit/odata/olingo/v4/processor/DataEntityProcessor.java:
--------------------------------------------------------------------------------
1 | package com.bloggingit.odata.olingo.v4.processor;
2 |
3 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaData;
4 | import com.bloggingit.odata.olingo.edm.meta.EntityMetaDataContainer;
5 | import com.bloggingit.odata.olingo.v4.service.OlingoDataService;
6 | import com.bloggingit.odata.olingo.v4.util.UriInfoUtil;
7 | import java.io.InputStream;
8 | import java.util.List;
9 | import java.util.Locale;
10 | import org.apache.olingo.commons.api.data.ContextURL;
11 | import org.apache.olingo.commons.api.data.Entity;
12 | import org.apache.olingo.commons.api.edm.EdmEntitySet;
13 | import org.apache.olingo.commons.api.edm.EdmEntityType;
14 | import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
15 | import org.apache.olingo.commons.api.format.ContentType;
16 | import org.apache.olingo.commons.api.http.HttpMethod;
17 | import org.apache.olingo.commons.api.http.HttpStatusCode;
18 | import org.apache.olingo.server.api.OData;
19 | import org.apache.olingo.server.api.ODataApplicationException;
20 | import org.apache.olingo.server.api.ODataLibraryException;
21 | import org.apache.olingo.server.api.ODataRequest;
22 | import org.apache.olingo.server.api.ODataResponse;
23 | import org.apache.olingo.server.api.ServiceMetadata;
24 | import org.apache.olingo.server.api.deserializer.DeserializerResult;
25 | import org.apache.olingo.server.api.deserializer.ODataDeserializer;
26 | import org.apache.olingo.server.api.processor.EntityProcessor;
27 | import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
28 | import org.apache.olingo.server.api.serializer.ODataSerializer;
29 | import org.apache.olingo.server.api.serializer.SerializerResult;
30 | import org.apache.olingo.server.api.uri.UriInfo;
31 | import org.apache.olingo.server.api.uri.UriParameter;
32 | import org.apache.olingo.server.api.uri.UriResource;
33 | import org.apache.olingo.server.api.uri.UriResourceEntitySet;
34 | import org.apache.olingo.server.api.uri.UriResourceNavigation;
35 |
36 | /**
37 | * This class is invoked by the Apache Olingo framework when the the OData
38 | * service is invoked order to display the data of a entity.
39 | *
40 | * This is the case if an Entity is requested by the user.
41 | */
42 | public class DataEntityProcessor extends AbstractEntityMetaDataProcessor implements EntityProcessor {
43 |
44 | private OData odata;
45 | private ServiceMetadata serviceMetadata;
46 | private final EntityMetaDataContainer entityMetaDataCollection;
47 |
48 | private final OlingoDataService dataService;
49 |
50 | public DataEntityProcessor(OlingoDataService dataService, EntityMetaDataContainer entityMetaDataCollection) {
51 | this.dataService = dataService;
52 | this.entityMetaDataCollection = entityMetaDataCollection;
53 | }
54 |
55 | @Override
56 | public void init(OData odata, ServiceMetadata serviceMetadata) {
57 | this.odata = odata;
58 | this.serviceMetadata = serviceMetadata;
59 | }
60 |
61 | @Override
62 | public void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
63 |
64 | Entity responseEntity = null; // required for serialization of the response body
65 | EdmEntitySet responseEdmEntitySet = null; // we need this for building the contextUrl
66 |
67 | // 1. retrieve the Entity Type
68 | // can be "normal" read operation, or navigation (to-one)
69 | //EdmEntitySet edmEntitySet = getUriResourceEdmEntitySet(uriInfo);
70 | List resourceParts = uriInfo.getUriResourceParts();
71 | int segmentCount = resourceParts.size();
72 |
73 | UriResource uriResource = resourceParts.get(0);
74 | if (!(uriResource instanceof UriResourceEntitySet)) {
75 | throw new ODataApplicationException("Only EntitySet is supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
76 | }
77 |
78 | UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) uriResource;
79 | EdmEntitySet startEdmEntitySet = uriResourceEntitySet.getEntitySet();
80 |
81 | // Analyze the URI segments
82 | if (segmentCount == 1) { // no navigation
83 | responseEdmEntitySet = startEdmEntitySet; // since we have only one segment
84 |
85 | // 2. step: retrieve the data from backend
86 | List keyPredicates = getUriResourceKeyPredicates(uriInfo);
87 |
88 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(responseEdmEntitySet.getName());
89 |
90 | responseEntity = this.dataService.getEntityData(meta, keyPredicates);
91 | } else if (segmentCount == 2) { //navigation
92 | UriResource navSegment = resourceParts.get(1);
93 |
94 | if (navSegment instanceof UriResourceNavigation) {
95 | UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) navSegment;
96 | EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
97 |
98 | responseEdmEntitySet = UriInfoUtil.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
99 |
100 | // 2nd: fetch the data from backend.
101 | List keyPredicates = uriResourceEntitySet.getKeyPredicates();
102 |
103 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(startEdmEntitySet.getName());
104 |
105 | Entity sourceEntity = this.dataService.getEntityData(meta, keyPredicates);
106 |
107 | responseEntity = this.dataService.getRelatedEntity(sourceEntity, uriResourceNavigation);
108 | }
109 | } else {
110 | // this would be the case for e.g. BookSet(1)/author/BookSet(1)/author
111 | throw new ODataApplicationException("Not supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
112 | }
113 |
114 | if (responseEntity == null) {
115 | throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
116 | }
117 |
118 | if (responseEdmEntitySet == null) {
119 | throw new ODataApplicationException("EntityType not found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
120 | }
121 |
122 | // 4. serialize
123 | EdmEntityType edmEntityType = responseEdmEntitySet.getEntityType();
124 |
125 | ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet).suffix(ContextURL.Suffix.ENTITY).build();
126 | EntitySerializerOptions options = EntitySerializerOptions.with().contextURL(contextUrl).build();
127 |
128 | ODataSerializer serializer = odata.createSerializer(responseFormat);
129 | SerializerResult serializerResult = serializer.entity(serviceMetadata, edmEntityType, responseEntity, options);
130 |
131 | //4. configure the response object
132 | setResponseContentAndOkStatus(response, serializerResult.getContent(), responseFormat);
133 | }
134 |
135 | @Override
136 | @SuppressWarnings("unchecked")
137 | public void createEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo,
138 | ContentType requestFormat, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
139 |
140 | // 1. Retrieve the entity type from the URI
141 | EdmEntitySet edmEntitySet = getUriResourceEdmEntitySet(uriInfo);
142 | EdmEntityType edmEntityType = edmEntitySet.getEntityType();
143 |
144 | // 2. create the data in backend
145 | // 2.1. retrieve the payload from the POST request for the entity to create and deserialize it
146 | InputStream requestInputStream = request.getBody();
147 | ODataDeserializer deserializer = this.odata.createDeserializer(requestFormat);
148 | DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
149 | Entity requestEntity = result.getEntity();
150 | // 2.2 do the creation in backend, which returns the newly created entity
151 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
152 |
153 | Entity createdEntity = this.dataService.createEntityData(meta, requestEntity);
154 |
155 | // 3. serialize the response (we have to return the created entity)
156 | ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build();
157 | // expand and select currently not supported
158 | EntitySerializerOptions options = EntitySerializerOptions.with().contextURL(contextUrl).build();
159 |
160 | ODataSerializer serializer = this.odata.createSerializer(responseFormat);
161 | SerializerResult serializedResponse = serializer.entity(serviceMetadata, edmEntityType, createdEntity, options);
162 |
163 | //4. configure the response object
164 | setResponseContentAndOkStatus(response, serializedResponse.getContent(), responseFormat);
165 | }
166 |
167 | @Override
168 | public void updateEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo,
169 | ContentType requestFormat, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
170 |
171 | // 1. Retrieve the entity set which belongs to the requested entity
172 | List resourcePaths = uriInfo.getUriResourceParts();
173 | // Note: only in our example we can assume that the first segment is the EntitySet
174 | UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0);
175 | EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
176 | EdmEntityType edmEntityType = edmEntitySet.getEntityType();
177 |
178 | // 2. update the data in backend
179 | // 2.1. retrieve the payload from the PUT request for the entity to be updated
180 | InputStream requestInputStream = request.getBody();
181 | ODataDeserializer deserializer = this.odata.createDeserializer(requestFormat);
182 | DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
183 | Entity requestEntity = result.getEntity();
184 | // 2.2 do the modification in backend
185 | List keyPredicates = uriResourceEntitySet.getKeyPredicates();
186 | // Note that this updateEntity()-method is invoked for both PUT or PATCH operations
187 | HttpMethod httpMethod = request.getMethod();
188 |
189 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
190 |
191 | this.dataService.updateEntityData(meta, keyPredicates, requestEntity, httpMethod);
192 |
193 | //3. configure the response object
194 | setResponseNoContentStatus(response);
195 | }
196 |
197 | @Override
198 | public void deleteEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException, ODataLibraryException {
199 |
200 | // 1. Retrieve the entity set which belongs to the requested entity
201 | List resourcePaths = uriInfo.getUriResourceParts();
202 | // Note: only in our example we can assume that the first segment is the EntitySet
203 | UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0);
204 | EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
205 |
206 | // 2. delete the data in backend
207 | EntityMetaData> meta = this.entityMetaDataCollection.getEntityMetaDataByTypeSetName(edmEntitySet.getName());
208 |
209 | List keyPredicates = uriResourceEntitySet.getKeyPredicates();
210 | this.dataService.deleteEntityData(meta.getEntityClass(), keyPredicates);
211 |
212 | //3. configure the response object
213 | setResponseNoContentStatus(response);
214 | }
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ODataV4 - JavaEE - Example - Apache Olingo
2 | ===========================================
3 |
4 | This example application is inspired by the `Basic Tutorial: Create an OData V4 Service with Olingo` which can be found in the [OData 4.0 Java Library Documentation](https://olingo.apache.org/doc/odata4/index.html).
5 | The implementation of the OData service is based on the [Apache Olingo OData 4.0 Library for Java](https://olingo.apache.org/doc/odata4/download.html).
6 |
7 | Afterwards the Web Application is deployed on a Java EE server, the OData service can be invoked from a browser or a http client and will provide the data according to the [OData V4 specification](http://www.odata.org/documentation). The service will display a list of books and a few properties that describe each book.
8 |
9 | Visit my blog for more background informations about this project.
10 |
11 | [OData V4 – Implementierung eines Services unter Java EE mittels Apache Olingo](https://www.blogging-it.com/odata-v4-implementierung-eines-services-unter-java-ee-mittels-apache-olingo/programmierung/java/javaee.html) (german)
12 |
13 |
14 | # Infrastructure
15 |
16 | - Java 1.8
17 | - Java EE
18 | - Maven
19 | - Apache TomEE
20 | - HTTP Web-Servlet
21 | - Apache Olingo 4.3.0 (2016-09-19)
22 |
23 |
24 | # Scenario
25 |
26 | The OData service in this example will implement the following simple sample model with the `Book` and `Author` entities.
27 |
28 | 
29 |
30 |
31 | # Custom Entity Annotations
32 |
33 | There is a `Annotation Processor` extension available for the Apache Olingo OData **2.0** library implementation. Unfortunately, there is currently no similar extension for the **4.0** library implementation. That is why I decided to create an own annotation processor, which provided support for elementary requirements for this example.
34 |
35 |
36 | # Persistence Data Provider
37 |
38 | At the moment there is no database connection implemented to provide data for the OData service. To keep it simple, the class `InMemoryDataStorage` provides an in-memory data storage.
39 |
40 | # Implemented OData Service Requests
41 |
42 | - read Service Document
43 | - read Metadata Document
44 | - read all book or author data
45 | - read single book or author data
46 | - read single book or author property value
47 | - read single book or author property value (plain text)
48 | - create new book or author
49 | - update existing book or author data
50 | - delete existing book or author data
51 | - count number of all books or authors data
52 |
53 |
54 |
55 | ## Request: Read Service Document
56 |
57 | Furthermore, OData specifies the usage of the so-called Service Document. The user can see which entity collections are offered by the OData service.
58 |
59 | Request:
60 |
61 | ```
62 | PATH: /
63 | METHOD: GET
64 | ```
65 |
66 | **Example**
67 |
68 | ```
69 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc
70 | ```
71 |
72 | Result:
73 |
74 | The expected result is the Service Document which displays informations of the entity container:
75 |
76 | ```json
77 | {
78 | "@odata.context": "$metadata",
79 | "value":
80 | [
81 | {
82 | "name": "AuthorSet",
83 | "url": "AuthorSet"
84 | },
85 | {
86 | "name": "BookSet",
87 | "url": "BookSet"
88 | }
89 | ]
90 | }
91 | ```
92 |
93 | ## Request: Read Metadata Document
94 |
95 | According to the OData specification, an OData service has to declare its structure in the so-called Metadata Document. This document defines the contract, such that the user of the service knows which requests can be executed, the structure of the result and how the service can be navigated.
96 |
97 | Request:
98 |
99 | ```
100 | PATH: /$metadata
101 | METHOD: GET
102 | ```
103 |
104 | **Example**
105 |
106 | ```
107 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/$metadata
108 | ```
109 |
110 | Result:
111 |
112 | The expected result is the Metadata Document that displays the Schema, EntityType, EntityContainer and the EntitySet.
113 |
114 | ```xml
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | ```
159 |
160 | ## Request: Read Book Or Author Entity Collection
161 |
162 | This request will display a list of books or authors and some properties that describe each entity.
163 |
164 | Add the optional `format` parameter to the request url, which contains information about the content type that is requested. This means that the user has the choice to receive the data either in XML or in JSON (default).
165 | If the content type is `application/json;odata.metadata=minimal`, then the payload is formatted in JSON.
166 | The content format can as well be specified via the request header `Accept: application/json;odata.metadata=minimal`.
167 |
168 | Internally the `DataCollectionProcessor#readEntityCollection` implementation of the OData service will be invoked.
169 |
170 | Request:
171 |
172 | ```
173 | PATH: /BookSet or /AuthorSet
174 | METHOD: GET
175 | Header (optional): Accept: application/json;odata.metadata=minimal
176 | ```
177 |
178 | **Example**
179 |
180 | ```
181 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet
182 |
183 | or
184 |
185 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet?$format=xml
186 |
187 | or
188 |
189 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet?$format=application/json;odata.metadata=minimal
190 | ```
191 |
192 | Result:
193 |
194 | The expected result is the list of book entries:
195 |
196 | ```json
197 | {
198 | "@odata.context": "$metadata#BookSet",
199 | "value":
200 | [
201 | {
202 | "title": "Book Title 1",
203 | "description": "This is the description of book 1",
204 | "releaseDate": "2011-07-21",
205 | "price": 9.95,
206 | "inStock": true,
207 | "id": 1
208 | },
209 | {
210 | "title": "Book Title 2",
211 | "description": "This is the description of book 2",
212 | "releaseDate": "2015-08-06",
213 | "price": 5.99,
214 | "inStock": true,
215 | "id": 2
216 | },
217 | {
218 | "title": "Book Title 3",
219 | "description": "This is the description of book 3",
220 | "releaseDate": "2013-05-12",
221 | "price": 14.5,
222 | "inStock": false,
223 | "id": 3
224 | }
225 | ]
226 | }
227 | ```
228 |
229 | ### System Query Option $count
230 |
231 | The `$count` query option allows to request a count of the matching resources. The number will be included with the resources in the response. The $count system query option with a value of `true` specifies that the total count of items within a collection matching the request be returned along with the result.
232 |
233 | OData V4 Spec Hint: The $count system query option ignores any $top, $skip, or $expand query options, and returns the total count of results across all pages including only those results matching any specified $filter and $search.
234 |
235 | **Example**
236 |
237 | ```
238 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet?$count=true
239 | ```
240 |
241 |
242 | ```json
243 | {
244 | "@odata.context": "$metadata#BookSet",
245 | "@odata.count": 3,
246 | "value":
247 | [
248 | {
249 | ...
250 | ```
251 |
252 |
253 | ## Request: Read Single Book Or Author Entity
254 |
255 | This request will display the details of a single book or author entity, which has the corresponding ID.
256 |
257 | Internally the `DataEntityProcessor#readEntity` implementation of the OData service will be invoked.
258 |
259 | Request:
260 |
261 | ```
262 | PATH: /BookSet(ID_OF_THE_BOOK) or /AuthorSet(id=ID_OF_THE_BOOK)
263 | METHOD: GET
264 | Header (optional): Accept: application/json;odata.metadata=minimal
265 | ```
266 |
267 | **Example**
268 |
269 | ```
270 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet(1)
271 | ```
272 |
273 | Result:
274 |
275 | The expected result is a response with the details of a single book with the id 1.
276 |
277 |
278 | ```json
279 | {
280 | "@odata.context": "$metadata#BookSet/$entity",
281 | "title": "Book Title 1",
282 | "description": "This is the description of book 1",
283 | "releaseDate": "2011-07-21",
284 | "price": 9.95,
285 | "inStock": true,
286 | "id": 1
287 | }
288 | ```
289 |
290 | ## Request: Read Single Book or Author Property
291 |
292 | If you don't want to receive the full payload of the entity, you can use this request to receive only the value of the property of the OData model you needed.
293 |
294 | Internally the `DataPrimitiveProcessor#readPrimitive` or `DataPrimitiveValueProcessor#readPrimitive` implementation of the OData service will be invoked.
295 |
296 | Request:
297 |
298 | ```
299 | PATH: /BookSet(ID_OF_THE_BOOK)/PROPERTY_NAME
300 | METHOD: GET
301 | Header (optional): Accept: application/json;odata.metadata=minimal
302 | ```
303 |
304 | **Example**
305 |
306 | ```
307 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet(1)/title
308 | ```
309 |
310 | Result:
311 |
312 | The expected result is a response with the title value of the book with the id 1.
313 |
314 | ```json
315 | {
316 | "@odata.context": "$metadata#BookSet/title",
317 | "value": "Book Title 1"
318 | }
319 | ```
320 |
321 | ### Plain Text Value
322 |
323 | If you use the `DataPrimitiveValueProcessor#readPrimitiveValue` implementation, it is also possible to request only the pure plain text value of a property.
324 |
325 | Request:
326 |
327 | ```
328 | PATH: /BookSet(ID_OF_THE_BOOK)/PROPERTY_NAME/$value
329 | METHOD: GET
330 | Header (optional): Accept: application/json;odata.metadata=minimal
331 | ```
332 |
333 | **Example**
334 |
335 | ```
336 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet(1)/title/$value
337 | ```
338 |
339 | Result:
340 |
341 | The expected result is a response with only the pure plain text value of the title property.
342 |
343 | ```text
344 | Book Title 1
345 | ```
346 |
347 | ## Request: Create New Book Or Author
348 |
349 | With this request we can create a new book (with author data) or a new author and add it to the available list.
350 | The Olingo library takes this request, serializes the request body and invokes the corresponding method of our processor class.
351 |
352 | Internally the `DataEntityProcessor#createEntity` implementation of the OData service will be invoked.
353 |
354 | Request:
355 |
356 | ```
357 | PATH: /BookSet or /AuthorSet
358 | METHOD: POST
359 | Header: Content-Type: application/json;odata.metadata=minimal
360 | Body: JSON data
361 | ```
362 |
363 | **Example - New Book**
364 |
365 | ```
366 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet
367 | ```
368 |
369 | Request Body:
370 |
371 | ```json
372 | {
373 | "title": "Book Title New",
374 | "description": "This is the description of the new book",
375 | "releaseDate": "2017-04-21",
376 | "author": {
377 | "name": "Author New",
378 | "gender": "FEMALE"
379 | },
380 | "price": 11.95,
381 | "inStock": true
382 | }
383 | ```
384 |
385 | Result:
386 |
387 | The result is a response with the details of the new book with the new assigned id.
388 |
389 | ```json
390 | {
391 | "@odata.context": "$metadata#BookSet",
392 | "title": "Book Title New",
393 | "description": "This is the description of the new book",
394 | "releaseDate": "2017-04-21",
395 | "price": 11.95,
396 | "inStock": true,
397 | "id": 4
398 | }
399 | ```
400 |
401 |
402 | **Example - New Author**
403 |
404 | ```
405 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/AuthorSet(1)
406 | ```
407 |
408 | Request Body:
409 |
410 | ```json
411 | {
412 | "name": "Author 5 New",
413 | "gender": "MALE",
414 | "contactInfo": {
415 | "eMail": "author5@test.xyz",
416 | "phone": "111/111"
417 | }
418 | }
419 | ```
420 |
421 | Result:
422 |
423 | The result is a response with the details of the new author with the new assigned id.
424 |
425 | ```json
426 | {
427 | "@odata.context": "$metadata#AuthorSet",
428 | "name": "Author 5 New",
429 | "gender": "MALE",
430 | "contactInfo":
431 | {
432 | "eMail": "author5@test.xyz",
433 | "phone": "111/111"
434 | },
435 | "id": 6
436 | }
437 | ```
438 |
439 | ## Request: Update Existing Book Or Author
440 |
441 | With this request we can update the values of an existing book or author. The update of an entity can be realized either with a `PUT` or a `PATCH` request.
442 |
443 | **PUT**: The value of the property is updated in the backend. The value of the other properties is set to null (exception: key properties can never be null).
444 |
445 | **PATCH**: The value of the property is updated in the backend. The values of the other properties remain untouched.
446 |
447 | The difference becomes relevant only in case if the user doesn’t send all the properties in the request body.
448 |
449 | Internally the `DataEntityProcessor#updateEntity` implementation of the OData service will be invoked.
450 |
451 | Request:
452 |
453 | ```
454 | PATH: /BookSet(ID_OF_THE_BOOK) or /AuthorSet(ID_OF_THE_AUTHOR)
455 | METHOD: PUT or PATCH
456 | Header: Content-Type: application/json;odata.metadata=minimal
457 | Body: JSON data
458 | ```
459 |
460 | **Example**
461 |
462 | ```
463 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet(2)
464 | ```
465 |
466 | Request Body:
467 |
468 | ```json
469 | {
470 | "title": "Book Title 2 Updated",
471 | "description": "This is the description of book 2 Updated"
472 | }
473 | ```
474 |
475 | Result:
476 |
477 | The OData service is not expected to return any response payload (HTTP status code to 204 – no content).
478 |
479 |
480 | ## Request: Delete Existing Book Or Author
481 |
482 | With this request we can remove data record of an existing book or author.
483 |
484 | Internally the `DataEntityProcessor#deleteEntity` implementation of the OData service will be invoked.
485 |
486 | Request:
487 |
488 | ```
489 | PATH: /BookSet(ID_OF_THE_BOOK) or /AuthorSet(ID_OF_THE_AUTHOR)
490 | METHOD: DELETE
491 | ```
492 |
493 | **Example**
494 |
495 | ```
496 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet(2)
497 | ```
498 |
499 | Result:
500 |
501 | The OData service is not expected to return any response payload (HTTP status code to 204 – no content).
502 |
503 |
504 | ## Request: Count Number Of All Books Or Authors Data
505 |
506 | To request only the number of items of a collection of entities or items of a collection-valued property, create a GET request with `/$count` appended to the resource path of the collection. On success, the response body MUST contain the exact count of items.
507 |
508 | Internally the `DataCollectionProcessor#countEntityCollection` implementation of the OData service will be invoked.
509 |
510 |
511 | Request:
512 |
513 | ```
514 | PATH: /BookSet/$count or /AuthorSet/$count
515 | METHOD: GET
516 | ```
517 |
518 | **Example**
519 |
520 | ```
521 | http://localhost:8080/odatav4-javaee-example-apache-olingo/api/servlet/v1/odatademo.svc/BookSet/$count
522 | ```
523 |
524 | Result:
525 |
526 | The result is a response with the count number of all books.
527 |
528 | ----------------------------------
529 | Markus Eschenbach
530 | [www.blogging-it.com](http://www.blogging-it.com)
531 |
532 |
--------------------------------------------------------------------------------