From 270582b25aac5b27b87281f9bdeaecf9e155c87f Mon Sep 17 00:00:00 2001 From: Philippe Date: Fri, 12 Apr 2019 00:16:38 -0300 Subject: [PATCH] [BAEL-1219] Code for Olingo V4 sample --- apache-olingo/olingo4/pom.xml | 2 +- .../olingo4/ODataServiceConfiguration.java | 31 +- .../examples/olingo4/ODataServlet.java | 2 +- .../examples/olingo4/edm/JpaEdmProvider.java | 33 +- .../JpaEntityCollectionProcessor.java | 77 ++-- .../olingo4/processor/JpaEntityMapper.java | 84 ++++ .../olingo4/processor/JpaEntityProcessor.java | 378 ++++++++++++++++++ .../repository/CarMakerRepository.java | 6 +- .../repository/CarModelRepository.java | 11 +- .../repository/EdmEntityRepository.java | 4 +- .../repository/RepositoryRegistry.java | 11 +- .../src/main/resources/application.properties | 3 + 12 files changed, 552 insertions(+), 90 deletions(-) create mode 100644 apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityMapper.java create mode 100644 apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityProcessor.java diff --git a/apache-olingo/olingo4/pom.xml b/apache-olingo/olingo4/pom.xml index a6f1fcdb7b..794aee0711 100644 --- a/apache-olingo/olingo4/pom.xml +++ b/apache-olingo/olingo4/pom.xml @@ -52,7 +52,7 @@ org.springframework.boot spring-boot-starter-web - + org.apache.olingo odata-server-api diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServiceConfiguration.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServiceConfiguration.java index bbac0ceac7..0cde665359 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServiceConfiguration.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServiceConfiguration.java @@ -14,33 +14,22 @@ import org.springframework.context.annotation.Configuration; @Configuration public class ODataServiceConfiguration { - - - @Bean - public ServletRegistrationBean odataServletRegistration(ODataHttpHandlerFactory factory, EntityManagerFactory emf) { - - ServletRegistrationBean bean = new ServletRegistrationBean( - odataServlet(factory,emf), - "/odata/*"); - bean.setLoadOnStartup(1); - return bean; - } - @Bean - public HttpServlet odataServlet(ODataHttpHandlerFactory factory, EntityManagerFactory emf ) { - - return new ODataServlet(factory,emf); + public ServletRegistrationBean odataServletRegistration(ODataHttpHandlerFactory factory) { + ServletRegistrationBean srb = + new ServletRegistrationBean<>(new ODataServlet(factory), "/odata/*"); + srb.setLoadOnStartup(1); + return srb; } - - + @Bean public ODataHttpHandlerFactory httpHandlerFactory(CsdlEdmProvider edmProvider, ODataFactory odataFactory, List processors) { return new ODataHttpHandlerFactoryImplBuilder() - .edmProvider(edmProvider) - .odataFactory(odataFactory) - .processors(processors) - .build(); + .edmProvider(edmProvider) + .odataFactory(odataFactory) + .processors(processors) + .build(); } } diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServlet.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServlet.java index c2adf2c1ae..c379124541 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServlet.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/ODataServlet.java @@ -25,7 +25,7 @@ public class ODataServlet extends HttpServlet { private final ODataHttpHandlerFactory odataHttpHandlerFactory; - public ODataServlet(ODataHttpHandlerFactory factory, EntityManagerFactory emf) { + public ODataServlet(ODataHttpHandlerFactory factory) { this.odataHttpHandlerFactory = factory; } diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/edm/JpaEdmProvider.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/edm/JpaEdmProvider.java index 3e490cdf58..68edb58e12 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/edm/JpaEdmProvider.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/edm/JpaEdmProvider.java @@ -62,8 +62,9 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider { if (e != null) { CsdlEntitySet entitySet = new CsdlEntitySet(); - entitySet.setName(entitySetName); - entitySet.setType(new FullQualifiedName(NAMESPACE, e.getName())); + entitySet + .setName(entitySetName) + .setType(new FullQualifiedName(NAMESPACE, e.getName())); return entitySet; } } @@ -135,7 +136,6 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider { .map((e) -> { try { // Here we use a simple mapping strategy to map entity types to entity set names: - // return getEntitySet(CONTAINER, e.getName() + "s"); } catch (ODataException oe) { throw new RuntimeException(oe); @@ -197,12 +197,22 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider { result.setKey(ids); - // Process navs + // Process 1:N navs List navs = et.getDeclaredPluralAttributes() .stream() .map(attr -> buildNavAttribute(et, attr)) .collect(Collectors.toList()); result.setNavigationProperties(navs); + + // Process N:1 navs + List navs2 = et.getDeclaredSingularAttributes() + .stream() + .filter(attr -> attr.getPersistentAttributeType() == PersistentAttributeType.MANY_TO_ONE) + .map(attr -> buildNavAttribute(et, attr)) + .collect(Collectors.toList()); + + result.getNavigationProperties().addAll(navs2); + return result; } @@ -225,13 +235,26 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider { return p; } + // Build NavProperty for 1:N or M:N associations private CsdlNavigationProperty buildNavAttribute(EntityType et, PluralAttribute attr) { CsdlNavigationProperty p = new CsdlNavigationProperty().setName(attr.getName()) .setType(new FullQualifiedName(NAMESPACE, attr.getBindableJavaType().getSimpleName())) - .setNullable(false); // for now + .setCollection(true) + .setNullable(false); return p; } + // Build NavProperty for N:1 associations + private CsdlNavigationProperty buildNavAttribute(EntityType et, SingularAttribute attr) { + + CsdlNavigationProperty p = new CsdlNavigationProperty().setName(attr.getName()) + .setType(new FullQualifiedName(NAMESPACE, attr.getBindableJavaType().getSimpleName())) + .setCollection(false) + .setNullable(attr.isOptional()); + + return p; + } + } diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java index ed921484c1..6bca0e9896 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java @@ -48,10 +48,12 @@ public class JpaEntityCollectionProcessor implements CountEntityCollectionProces private ServiceMetadata serviceMetadata; private EntityManagerFactory emf; private RepositoryRegistry repositoryRegistry; + private JpaEntityMapper entityMapper; - public JpaEntityCollectionProcessor(EntityManagerFactory emf, RepositoryRegistry repositoryRegistry) { + public JpaEntityCollectionProcessor(EntityManagerFactory emf, RepositoryRegistry repositoryRegistry, JpaEntityMapper entityMapper) { this.emf = emf; this.repositoryRegistry = repositoryRegistry; + this.entityMapper = entityMapper; } @Override @@ -115,71 +117,40 @@ public class JpaEntityCollectionProcessor implements CountEntityCollectionProces } - private EntityCollection getData(EdmEntitySet edmEntitySet, UriInfo uriInfo) { + /** + * Helper method to retrieve all entities of an entity set from an the backend database + * @param edmEntitySet + * @param uriInfo + * @return + */ + protected EntityCollection getData(EdmEntitySet edmEntitySet, UriInfo uriInfo) { EdmEntityType type = edmEntitySet.getEntityType(); - JpaRepository repo = repositoryRegistry.getRepositoryForEntity(type); + JpaRepository repo = (JpaRepository)repositoryRegistry.getRepositoryForEntity(type); EntityCollection result = new EntityCollection(); repo.findAll() .stream() .forEach((it) -> result.getEntities() - .add(map2entity(edmEntitySet, it))); + .add(entityMapper.map2entity(edmEntitySet, it))); return result; } - private Entity map2entity(EdmEntitySet edmEntitySet, Object entry) { - - EntityType et = emf.getMetamodel() - .entity(entry.getClass()); - - - Entity e = new Entity(); - try { - // First, we set the entity's primary key - - // Now map all properties - et.getDeclaredSingularAttributes().stream() - .forEach( (attr) -> { - if ( !attr.isAssociation()) { - Object v = getPropertyValue(entry,attr.getName()); - Property p = new Property(null, attr.getName(),ValueType.PRIMITIVE,v); - e.addProperty(p); - - if ( attr.isId()) { - e.setId(createId(edmEntitySet.getName(),v)); - } - } - - }); - - } catch (Exception ex) { - throw new ODataRuntimeException("[E141] Unable to create OData entity", ex); - } - - return e; - } - - private Object getPropertyValue(Object entry, String name) { - try { - return PropertyUtils.getProperty(entry,name); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new ODataRuntimeException("[E141] Unable to read property from entity, property=" + name, e); - } - } - - private URI createId(String entitySetName, Object id) { - try { - return new URI(entitySetName + "(" + String.valueOf(id) + ")"); - } catch (URISyntaxException e) { - throw new ODataRuntimeException("[E177] Unable to create URI", e); - } - } + /** + * Helper method to get the total size of an entity set + * @param edmEntitySet + * @param uriInfo + * @return + */ private Long getCount(EdmEntitySet edmEntitySet, UriInfo uriInfo) { - // TODO Auto-generated method stub - return null; + + EdmEntityType type = edmEntitySet.getEntityType(); + JpaRepository repo = (JpaRepository)repositoryRegistry.getRepositoryForEntity(type); + EntityCollection result = new EntityCollection(); + + return repo.count(); } } diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityMapper.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityMapper.java new file mode 100644 index 0000000000..5fd863124f --- /dev/null +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityMapper.java @@ -0,0 +1,84 @@ +/** + * + */ +package org.baeldung.examples.olingo4.processor; + +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.metamodel.EntityType; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ValueType; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.ex.ODataRuntimeException; +import org.springframework.stereotype.Component; + +/** + *

Helper class that converts a JPA entity into an OData entity using + * available metadata from the JPA's EntityManagerFactory.

+ * + * @author Philippe + * + */ +@Component +public class JpaEntityMapper { + + private EntityManagerFactory emf; + + public JpaEntityMapper(EntityManagerFactory emf) { + this.emf = emf; + } + + + public Entity map2entity(EdmEntitySet edmEntitySet, Object entry) { + + EntityType et = emf.getMetamodel() + .entity(entry.getClass()); + + + Entity e = new Entity(); + try { + et.getDeclaredSingularAttributes().stream() + .forEach( (attr) -> { + if ( !attr.isAssociation()) { + Object v = getPropertyValue(entry,attr.getName()); + Property p = new Property(null, attr.getName(),ValueType.PRIMITIVE,v); + e.addProperty(p); + + if ( attr.isId()) { + e.setId(createId(edmEntitySet.getName(),v)); + } + } + }); + } catch (Exception ex) { + throw new ODataRuntimeException("[E141] Unable to create OData entity", ex); + } + + return e; + } + + + private Object getPropertyValue(Object entry, String name) { + try { + return PropertyUtils.getProperty(entry,name); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new ODataRuntimeException("[E141] Unable to read property from entity, property=" + name, e); + } + } + + private URI createId(String entitySetName, Object id) { + try { + return new URI(entitySetName + "(" + String.valueOf(id) + ")"); + } catch (URISyntaxException e) { + throw new ODataRuntimeException("[E177] Unable to create URI", e); + } + } + + + +} diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityProcessor.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityProcessor.java new file mode 100644 index 0000000000..23a5a7ac4b --- /dev/null +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityProcessor.java @@ -0,0 +1,378 @@ +/** + * + */ +package org.baeldung.examples.olingo4.processor; + +import java.io.InputStream; +import java.lang.reflect.Member; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.metamodel.SingularAttribute; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntityCollection; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; +import org.apache.olingo.commons.api.edm.EdmType; +import org.apache.olingo.commons.api.ex.ODataRuntimeException; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataLibraryException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.processor.EntityProcessor; +import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions; +import org.apache.olingo.server.api.serializer.EntitySerializerOptions; +import org.apache.olingo.server.api.serializer.ODataSerializer; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.serializer.SerializerResult; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriParameter; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.UriResourceNavigation; +import org.baeldung.examples.olingo4.repository.EdmEntityRepository; +import org.baeldung.examples.olingo4.repository.RepositoryRegistry; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Component; + +/** + * JpaEntityProcessor adapter. + *

This implementation is heavily based on the Tutorial available + * at Olingo's site. It is meant to be an starting point for an actual implementation.

+ *

Please note that many features from a full-fledged are missing + * @author Philippe + * + */ +@Component +public class JpaEntityProcessor implements EntityProcessor { + + private EntityManagerFactory emf; + private OData odata; + private ServiceMetadata serviceMetadata; + private RepositoryRegistry registry; + private JpaEntityMapper entityMapper; + + public JpaEntityProcessor(EntityManagerFactory emf, RepositoryRegistry registry, JpaEntityMapper entityMapper) { + this.emf = emf; + this.registry = registry; + this.entityMapper = entityMapper; + } + + /* (non-Javadoc) + * @see org.apache.olingo.server.api.processor.Processor#init(org.apache.olingo.server.api.OData, org.apache.olingo.server.api.ServiceMetadata) + */ + @Override + public void init(OData odata, ServiceMetadata serviceMetadata) { + this.odata = odata; + this.serviceMetadata = serviceMetadata; + + } + + /* (non-Javadoc) + * @see org.apache.olingo.server.api.processor.EntityProcessor#readEntity(org.apache.olingo.server.api.ODataRequest, org.apache.olingo.server.api.ODataResponse, org.apache.olingo.server.api.uri.UriInfo, org.apache.olingo.commons.api.format.ContentType) + */ + @Override + public void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException { + + // First, we have to figure out which entity is requested + List resourceParts = uriInfo.getUriResourceParts(); + InputStream entityStream; + + UriResourceEntitySet rootResourceEntitySet = (UriResourceEntitySet) resourceParts.get(0); + EdmEntitySet rootEntitySet = rootResourceEntitySet.getEntitySet(); + List rootPredicates = rootResourceEntitySet.getKeyPredicates(); + EdmEntityType rootEntityType = rootEntitySet.getEntityType(); + + + if ( resourceParts.size() == 1 ) { + entityStream = readEntity(rootEntitySet,rootPredicates,responseFormat); + } + else if ( resourceParts.size() == 2 ) { + UriResource part = resourceParts.get(1); + if ( !(part instanceof UriResourceNavigation)) { + throw new ODataRuntimeException("[E103] part type not supported: class=" + part.getClass().getName()); + } + + UriResourceNavigation navSegment = (UriResourceNavigation)part; + + // We have three scenarios we must handle: + // Entity(x)/Related, where Related is a 1:N or M:N relationship => result is a collection + // Entity(x)/Related, where Related is a N:1 or 1:1 relationship => result is a single entity + // Entity(x)/Related(z), where Related is a 1:N or M:N relationship => result is a single entity + if (navSegment.getKeyPredicates().isEmpty()) { + if ( isOne2ManyProperty(rootEntityType,navSegment.getProperty())) { + entityStream = readRelatedEntities(rootEntitySet,rootPredicates,navSegment.getProperty(),responseFormat); + } + else { + // The relation must point to another entity type, so casting should be safe here + EdmEntityType resultType = (EdmEntityType)rootEntityType.getNavigationProperty(navSegment.getProperty().getName()).getType(); + EdmEntitySet resultEntitySet = entitySetFromType(resultType); + + entityStream = readEntity(resultEntitySet, navSegment.getKeyPredicates(), responseFormat); + } + } + else { + entityStream = readRelatedEntity(request, rootEntitySet,rootPredicates,navSegment.getProperty(),navSegment.getKeyPredicates(),responseFormat); + } + + } + else { + // For now, we'll only allow navigation just to directly linked navs + throw new ODataApplicationException("[E109] Multi-level navigation not supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + //4. configure the response object + response.setContent(entityStream); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); + + } + + + // Lookup the EntitySet associated with an EntityType + // In our example, we assume we have only one entityset for each entity type + private EdmEntitySet entitySetFromType(EdmEntityType type) { + return serviceMetadata + .getEdm() + .getEntityContainer() + .getEntitySets() + .stream() + .filter((s) -> s.getEntityType().getName().equals(type.getName())) + .findFirst() + .orElseThrow(() -> new ODataRuntimeException("[E144] No entity set found for type " + type.getFullQualifiedName())); + } + + // + private boolean isOne2ManyProperty(EdmEntityType entityType, EdmNavigationProperty property) { + return entityType.getProperty(property.getName()) != null && property.isCollection(); + } + + private InputStream readEntity(EdmEntitySet entitySet, List keyPredicates,ContentType responseFormat) throws ODataApplicationException, ODataLibraryException { + + Entity entity = readEntityData(entitySet,keyPredicates); + + ContextURL contextUrl = ContextURL.with().entitySet(entitySet).build(); + // expand and select currently not supported + EntitySerializerOptions options = EntitySerializerOptions + .with() + .contextURL(contextUrl) + .build(); + + ODataSerializer serializer = odata.createSerializer(responseFormat); + + SerializerResult serializerResult = serializer.entity(serviceMetadata, entitySet.getEntityType(), entity, options); + return serializerResult.getContent(); + } + + private InputStream readRelatedEntities(EdmEntitySet rootEntitySet, List rootPredicates, EdmNavigationProperty property, ContentType responseFormat) throws ODataApplicationException { + + Object jpaEntity = readJPAEntity(rootEntitySet, rootPredicates); + try { + Object set = PropertyUtils.getProperty(jpaEntity, property.getName()); + EdmEntitySet entitySet = entitySetFromType(property.getType()); + ContextURL contextUrl = ContextURL + .with() + .entitySet(entitySet) + .build(); + + EntityCollectionSerializerOptions options = EntityCollectionSerializerOptions + .with() + .contextURL(contextUrl) + .build(); + + EntityCollection result = new EntityCollection(); + + ((Collection)set) + .stream() + .map((o) -> this.entityMapper.map2entity(entitySet, o)) + .forEach((e) -> result.getEntities().add(e)); + + ODataSerializer serializer = odata.createSerializer(responseFormat); + SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, property.getType(), result, options); + return serializerResult.getContent(); + } + catch(Exception ex) { + throw new ODataRuntimeException("[E181] Error accessing database", ex); + } + } + + @SuppressWarnings({ "rawtypes", "serial", "unchecked" }) + private InputStream readRelatedEntity(ODataRequest request, EdmEntitySet rootEntitySet, List rootPredicates, EdmNavigationProperty property, List predicates, ContentType responseFormat) throws ODataApplicationException, SerializerException { + + + JpaSpecificationExecutor rootRepo = (JpaSpecificationExecutor)registry.getRepositoryForEntity(rootEntitySet.getEntityType()); + JpaSpecificationExecutor repo = (JpaSpecificationExecutor)registry.getRepositoryForEntity(property.getType()); + + // We assume here that we have a bi-directional 1:N relationship, so we'll + // always have a property in the child entity that points to the parent + Class rootClass = ((EdmEntityRepository)rootRepo).getEntityClass(); + Class childClass = ((EdmEntityRepository)repo).getEntityClass(); + SingularAttribute fk = emf.getMetamodel() + .entity(childClass) + .getSingularAttributes() + .stream() + .filter((attr) -> attr.isAssociation() && attr.getJavaType().isAssignableFrom(rootClass)) + .findFirst() + .orElse(null); + + SingularAttribute pk = emf.getMetamodel() + .entity(childClass) + .getId(Long.class); + + SingularAttribute rootPk = emf.getMetamodel() + .entity(rootClass) + .getId(Long.class); + + if ( fk == null ) { + throw new ODataRuntimeException("[E230] No singular attribute of child class '" + childClass.getName() + "' found" ); + } + + + Specification spec = new Specification() { + @Override + public Predicate toPredicate(Root root, CriteriaQuery q, CriteriaBuilder cb) { + + try { + Object rootInstance = rootClass.newInstance(); + PropertyUtils.setProperty(rootInstance, rootPk.getName(), getEntityKey(rootEntitySet.getEntityType(),rootPredicates)); + + final Predicate p = cb.and( + cb.equal( + root.get(pk), + getEntityKey(property.getType(),predicates)), + cb.equal( + root.get(fk), + rootInstance)); + + return p; + } + catch(Exception ex) { + throw new ODataRuntimeException(ex); + } + + } + }; + + // Read data from DB + EdmEntitySet relatedEntitySet = entitySetFromType(property.getType()); + EntityCollection data = new EntityCollection(); + + repo.findAll(spec) + .stream() + .forEach((entry) -> data.getEntities().add(entityMapper.map2entity(relatedEntitySet, entry))); + + // + + ODataSerializer serializer = odata.createSerializer(responseFormat); + + // 4th: Now serialize the content: transform from the EntitySet object to InputStream + EdmEntityType edmEntityType = relatedEntitySet.getEntityType(); + ContextURL contextUrl = ContextURL.with() + .entitySet(relatedEntitySet) + .build(); + + final String id = request.getRawBaseUri() + "/" + relatedEntitySet.getName(); + EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with() + .id(id) + .contextURL(contextUrl) + .build(); + SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType, data, opts); + InputStream serializedContent = serializerResult.getContent(); + + + return serializedContent; + } + + + /** + * This method returns a speficic entity given its primary key + * @param edmEntitySet + * @param keyPredicates + * @return + */ + protected Entity readEntityData(EdmEntitySet edmEntitySet, List keyPredicates) throws ODataApplicationException { + + Object jpaEntry = readJPAEntity(edmEntitySet, keyPredicates); + Entity e = entityMapper.map2entity(edmEntitySet, jpaEntry); + return e; + } + + private Object readJPAEntity(EdmEntitySet edmEntitySet, List keyPredicates) throws ODataApplicationException { + EdmEntityType type = edmEntitySet.getEntityType(); + JpaRepository repo = (JpaRepository)registry.getRepositoryForEntity(type); + + // Get key value + Object keyValue = getEntityKey(type,keyPredicates); + Object entry = repo + .findById(keyValue) + .orElseThrow( + () -> new ODataApplicationException("[E116] NO entity found for the given key", + HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH)); + + return entry; + } + + private Object getEntityKey(EdmEntityType type, List keyPredicates) { + + if ( keyPredicates.size() > 1 ) { + throw new ODataRuntimeException("[E131] Composite keys are not supported"); + } + + // For now, we'll assume we only have numeric keys. + UriParameter keyParam = keyPredicates.get(0); + try { + return Long.parseLong(keyParam.getText()); + } + catch(NumberFormatException nfe) { + throw new ODataRuntimeException("[E140] Invalid key value. Only numeric keys are supported by this service"); + } + + + } + + /* (non-Javadoc) + * @see org.apache.olingo.server.api.processor.EntityProcessor#createEntity(org.apache.olingo.server.api.ODataRequest, org.apache.olingo.server.api.ODataResponse, org.apache.olingo.server.api.uri.UriInfo, org.apache.olingo.commons.api.format.ContentType, org.apache.olingo.commons.api.format.ContentType) + */ + @Override + public void createEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestFormat, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.apache.olingo.server.api.processor.EntityProcessor#updateEntity(org.apache.olingo.server.api.ODataRequest, org.apache.olingo.server.api.ODataResponse, org.apache.olingo.server.api.uri.UriInfo, org.apache.olingo.commons.api.format.ContentType, org.apache.olingo.commons.api.format.ContentType) + */ + @Override + public void updateEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestFormat, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.apache.olingo.server.api.processor.EntityProcessor#deleteEntity(org.apache.olingo.server.api.ODataRequest, org.apache.olingo.server.api.ODataResponse, org.apache.olingo.server.api.uri.UriInfo) + */ + @Override + public void deleteEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException, ODataLibraryException { + // TODO Auto-generated method stub + + } + +} diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarMakerRepository.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarMakerRepository.java index 70664bcf86..1bde9f148c 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarMakerRepository.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarMakerRepository.java @@ -6,7 +6,11 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; @Repository -public interface CarMakerRepository extends JpaRepository, JpaSpecificationExecutor, EdmEntityRepository { +public interface CarMakerRepository extends EdmEntityRepository, JpaRepository, JpaSpecificationExecutor { public default String getEdmEntityName() { return CarMaker.class.getSimpleName();} + @Override + default Class getEntityClass() { + return CarMaker.class; + } } diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarModelRepository.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarModelRepository.java index 40590b15e1..247bf6e77b 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarModelRepository.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/CarModelRepository.java @@ -1,13 +1,22 @@ package org.baeldung.examples.olingo4.repository; +import java.util.List; + import org.baeldung.examples.olingo4.domain.CarModel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; @Repository -public interface CarModelRepository extends JpaRepository, JpaSpecificationExecutor,EdmEntityRepository { +public interface CarModelRepository extends EdmEntityRepository, JpaRepository, JpaSpecificationExecutor { + public List findByMakerId(Long makerId); + public default String getEdmEntityName() { return CarModel.class.getSimpleName();} + + @Override + default Class getEntityClass() { + return CarModel.class; + } } diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/EdmEntityRepository.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/EdmEntityRepository.java index 105925c958..dbfd0e6f93 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/EdmEntityRepository.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/EdmEntityRepository.java @@ -8,8 +8,10 @@ package org.baeldung.examples.olingo4.repository; * @author Philippe * */ -public interface EdmEntityRepository { +public interface EdmEntityRepository { public String getEdmEntityName(); + public Class getEntityClass(); + } diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/RepositoryRegistry.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/RepositoryRegistry.java index 991eb21fc2..20981fdfef 100644 --- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/RepositoryRegistry.java +++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/repository/RepositoryRegistry.java @@ -5,24 +5,23 @@ import java.util.List; import java.util.Map; import org.apache.olingo.commons.api.edm.EdmEntityType; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; @Component public class RepositoryRegistry { - private Map> repositoriesByClassName = new HashMap<>(); + private Map> repositoriesByClassName = new HashMap<>(); - public RepositoryRegistry(List allRepositories) { + public RepositoryRegistry(List> allRepositories) { allRepositories.stream().forEach((r) -> - repositoriesByClassName.put(r.getEdmEntityName(),(JpaRepository)r)); + repositoriesByClassName.put(r.getEdmEntityName(),(EdmEntityRepository)r)); } - public JpaRepository getRepositoryForEntity(EdmEntityType entityType) { - JpaRepository repo = repositoriesByClassName.get(entityType.getName()); + public EdmEntityRepository getRepositoryForEntity(EdmEntityType entityType) { + EdmEntityRepository repo = repositoriesByClassName.get(entityType.getName()); return repo; } } diff --git a/apache-olingo/olingo4/src/main/resources/application.properties b/apache-olingo/olingo4/src/main/resources/application.properties index 6f115b5ae4..e4742b4fbb 100644 --- a/apache-olingo/olingo4/src/main/resources/application.properties +++ b/apache-olingo/olingo4/src/main/resources/application.properties @@ -1,3 +1,6 @@ +server: + port: 8080 + spring: jpa: show-sql: true