[BAEL-1219] Code for Olingo V4 sample
This commit is contained in:
parent
6ac9c01d3d
commit
270582b25a
|
@ -52,7 +52,7 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.olingo</groupId>
|
<groupId>org.apache.olingo</groupId>
|
||||||
<artifactId>odata-server-api</artifactId>
|
<artifactId>odata-server-api</artifactId>
|
||||||
|
|
|
@ -14,33 +14,22 @@ import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class ODataServiceConfiguration {
|
public class ODataServiceConfiguration {
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ServletRegistrationBean<HttpServlet> odataServletRegistration(ODataHttpHandlerFactory factory, EntityManagerFactory emf) {
|
|
||||||
|
|
||||||
ServletRegistrationBean bean = new ServletRegistrationBean(
|
|
||||||
odataServlet(factory,emf),
|
|
||||||
"/odata/*");
|
|
||||||
bean.setLoadOnStartup(1);
|
|
||||||
return bean;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public HttpServlet odataServlet(ODataHttpHandlerFactory factory, EntityManagerFactory emf ) {
|
public ServletRegistrationBean<HttpServlet> odataServletRegistration(ODataHttpHandlerFactory factory) {
|
||||||
|
ServletRegistrationBean<HttpServlet> srb =
|
||||||
return new ODataServlet(factory,emf);
|
new ServletRegistrationBean<>(new ODataServlet(factory), "/odata/*");
|
||||||
|
srb.setLoadOnStartup(1);
|
||||||
|
return srb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ODataHttpHandlerFactory httpHandlerFactory(CsdlEdmProvider edmProvider, ODataFactory odataFactory, List<Processor> processors) {
|
public ODataHttpHandlerFactory httpHandlerFactory(CsdlEdmProvider edmProvider, ODataFactory odataFactory, List<Processor> processors) {
|
||||||
return new ODataHttpHandlerFactoryImplBuilder()
|
return new ODataHttpHandlerFactoryImplBuilder()
|
||||||
.edmProvider(edmProvider)
|
.edmProvider(edmProvider)
|
||||||
.odataFactory(odataFactory)
|
.odataFactory(odataFactory)
|
||||||
.processors(processors)
|
.processors(processors)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class ODataServlet extends HttpServlet {
|
||||||
private final ODataHttpHandlerFactory odataHttpHandlerFactory;
|
private final ODataHttpHandlerFactory odataHttpHandlerFactory;
|
||||||
|
|
||||||
|
|
||||||
public ODataServlet(ODataHttpHandlerFactory factory, EntityManagerFactory emf) {
|
public ODataServlet(ODataHttpHandlerFactory factory) {
|
||||||
this.odataHttpHandlerFactory = factory;
|
this.odataHttpHandlerFactory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,8 +62,9 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider {
|
||||||
|
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
CsdlEntitySet entitySet = new CsdlEntitySet();
|
CsdlEntitySet entitySet = new CsdlEntitySet();
|
||||||
entitySet.setName(entitySetName);
|
entitySet
|
||||||
entitySet.setType(new FullQualifiedName(NAMESPACE, e.getName()));
|
.setName(entitySetName)
|
||||||
|
.setType(new FullQualifiedName(NAMESPACE, e.getName()));
|
||||||
return entitySet;
|
return entitySet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +136,6 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider {
|
||||||
.map((e) -> {
|
.map((e) -> {
|
||||||
try {
|
try {
|
||||||
// Here we use a simple mapping strategy to map entity types to entity set names:
|
// Here we use a simple mapping strategy to map entity types to entity set names:
|
||||||
//
|
|
||||||
return getEntitySet(CONTAINER, e.getName() + "s");
|
return getEntitySet(CONTAINER, e.getName() + "s");
|
||||||
} catch (ODataException oe) {
|
} catch (ODataException oe) {
|
||||||
throw new RuntimeException(oe);
|
throw new RuntimeException(oe);
|
||||||
|
@ -197,12 +197,22 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider {
|
||||||
|
|
||||||
result.setKey(ids);
|
result.setKey(ids);
|
||||||
|
|
||||||
// Process navs
|
// Process 1:N navs
|
||||||
List<CsdlNavigationProperty> navs = et.getDeclaredPluralAttributes()
|
List<CsdlNavigationProperty> navs = et.getDeclaredPluralAttributes()
|
||||||
.stream()
|
.stream()
|
||||||
.map(attr -> buildNavAttribute(et, attr))
|
.map(attr -> buildNavAttribute(et, attr))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
result.setNavigationProperties(navs);
|
result.setNavigationProperties(navs);
|
||||||
|
|
||||||
|
// Process N:1 navs
|
||||||
|
List<CsdlNavigationProperty> 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -225,13 +235,26 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build NavProperty for 1:N or M:N associations
|
||||||
private CsdlNavigationProperty buildNavAttribute(EntityType<?> et, PluralAttribute<?, ?, ?> attr) {
|
private CsdlNavigationProperty buildNavAttribute(EntityType<?> et, PluralAttribute<?, ?, ?> attr) {
|
||||||
|
|
||||||
CsdlNavigationProperty p = new CsdlNavigationProperty().setName(attr.getName())
|
CsdlNavigationProperty p = new CsdlNavigationProperty().setName(attr.getName())
|
||||||
.setType(new FullQualifiedName(NAMESPACE, attr.getBindableJavaType().getSimpleName()))
|
.setType(new FullQualifiedName(NAMESPACE, attr.getBindableJavaType().getSimpleName()))
|
||||||
.setNullable(false); // for now
|
.setCollection(true)
|
||||||
|
.setNullable(false);
|
||||||
|
|
||||||
return p;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,10 +48,12 @@ public class JpaEntityCollectionProcessor implements CountEntityCollectionProces
|
||||||
private ServiceMetadata serviceMetadata;
|
private ServiceMetadata serviceMetadata;
|
||||||
private EntityManagerFactory emf;
|
private EntityManagerFactory emf;
|
||||||
private RepositoryRegistry repositoryRegistry;
|
private RepositoryRegistry repositoryRegistry;
|
||||||
|
private JpaEntityMapper entityMapper;
|
||||||
|
|
||||||
public JpaEntityCollectionProcessor(EntityManagerFactory emf, RepositoryRegistry repositoryRegistry) {
|
public JpaEntityCollectionProcessor(EntityManagerFactory emf, RepositoryRegistry repositoryRegistry, JpaEntityMapper entityMapper) {
|
||||||
this.emf = emf;
|
this.emf = emf;
|
||||||
this.repositoryRegistry = repositoryRegistry;
|
this.repositoryRegistry = repositoryRegistry;
|
||||||
|
this.entityMapper = entityMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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();
|
EdmEntityType type = edmEntitySet.getEntityType();
|
||||||
JpaRepository<?, ?> repo = repositoryRegistry.getRepositoryForEntity(type);
|
JpaRepository<?, ?> repo = (JpaRepository<?, ?>)repositoryRegistry.getRepositoryForEntity(type);
|
||||||
EntityCollection result = new EntityCollection();
|
EntityCollection result = new EntityCollection();
|
||||||
|
|
||||||
repo.findAll()
|
repo.findAll()
|
||||||
.stream()
|
.stream()
|
||||||
.forEach((it) -> result.getEntities()
|
.forEach((it) -> result.getEntities()
|
||||||
.add(map2entity(edmEntitySet, it)));
|
.add(entityMapper.map2entity(edmEntitySet, it)));
|
||||||
|
|
||||||
return result;
|
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) {
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Helper class that converts a JPA entity into an OData entity using
|
||||||
|
* available metadata from the JPA's EntityManagerFactory.</p>
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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.
|
||||||
|
* <p>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.</p>
|
||||||
|
* <p>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<UriResource> resourceParts = uriInfo.getUriResourceParts();
|
||||||
|
InputStream entityStream;
|
||||||
|
|
||||||
|
UriResourceEntitySet rootResourceEntitySet = (UriResourceEntitySet) resourceParts.get(0);
|
||||||
|
EdmEntitySet rootEntitySet = rootResourceEntitySet.getEntitySet();
|
||||||
|
List<UriParameter> 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<UriParameter> 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<UriParameter> 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<Object>)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<UriParameter> rootPredicates, EdmNavigationProperty property, List<UriParameter> predicates, ContentType responseFormat) throws ODataApplicationException, SerializerException {
|
||||||
|
|
||||||
|
|
||||||
|
JpaSpecificationExecutor<Object> rootRepo = (JpaSpecificationExecutor<Object>)registry.getRepositoryForEntity(rootEntitySet.getEntityType());
|
||||||
|
JpaSpecificationExecutor<Object> repo = (JpaSpecificationExecutor<Object>)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<UriParameter> keyPredicates) throws ODataApplicationException {
|
||||||
|
|
||||||
|
Object jpaEntry = readJPAEntity(edmEntitySet, keyPredicates);
|
||||||
|
Entity e = entityMapper.map2entity(edmEntitySet, jpaEntry);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readJPAEntity(EdmEntitySet edmEntitySet, List<UriParameter> keyPredicates) throws ODataApplicationException {
|
||||||
|
EdmEntityType type = edmEntitySet.getEntityType();
|
||||||
|
JpaRepository<Object,Object> repo = (JpaRepository<Object,Object>)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<UriParameter> 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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,11 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface CarMakerRepository extends JpaRepository<CarMaker, Long>, JpaSpecificationExecutor<CarMaker>, EdmEntityRepository {
|
public interface CarMakerRepository extends EdmEntityRepository<CarMaker>, JpaRepository<CarMaker, Long>, JpaSpecificationExecutor<CarMaker> {
|
||||||
|
|
||||||
public default String getEdmEntityName() { return CarMaker.class.getSimpleName();}
|
public default String getEdmEntityName() { return CarMaker.class.getSimpleName();}
|
||||||
|
@Override
|
||||||
|
default Class<CarMaker> getEntityClass() {
|
||||||
|
return CarMaker.class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
package org.baeldung.examples.olingo4.repository;
|
package org.baeldung.examples.olingo4.repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.baeldung.examples.olingo4.domain.CarModel;
|
import org.baeldung.examples.olingo4.domain.CarModel;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface CarModelRepository extends JpaRepository<CarModel, Long>, JpaSpecificationExecutor<CarModel>,EdmEntityRepository {
|
public interface CarModelRepository extends EdmEntityRepository<CarModel>, JpaRepository<CarModel, Long>, JpaSpecificationExecutor<CarModel> {
|
||||||
|
|
||||||
|
public List<CarModel> findByMakerId(Long makerId);
|
||||||
|
|
||||||
public default String getEdmEntityName() { return CarModel.class.getSimpleName();}
|
public default String getEdmEntityName() { return CarModel.class.getSimpleName();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Class<CarModel> getEntityClass() {
|
||||||
|
return CarModel.class;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@ package org.baeldung.examples.olingo4.repository;
|
||||||
* @author Philippe
|
* @author Philippe
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface EdmEntityRepository {
|
public interface EdmEntityRepository<E> {
|
||||||
|
|
||||||
public String getEdmEntityName();
|
public String getEdmEntityName();
|
||||||
|
public Class<E> getEntityClass();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,24 +5,23 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.olingo.commons.api.edm.EdmEntityType;
|
import org.apache.olingo.commons.api.edm.EdmEntityType;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class RepositoryRegistry {
|
public class RepositoryRegistry {
|
||||||
|
|
||||||
private Map<String,JpaRepository<?,?>> repositoriesByClassName = new HashMap<>();
|
private Map<String,EdmEntityRepository<?>> repositoriesByClassName = new HashMap<>();
|
||||||
|
|
||||||
public RepositoryRegistry(List<EdmEntityRepository> allRepositories) {
|
public RepositoryRegistry(List<EdmEntityRepository<?>> allRepositories) {
|
||||||
|
|
||||||
allRepositories.stream().forEach((r) ->
|
allRepositories.stream().forEach((r) ->
|
||||||
repositoriesByClassName.put(r.getEdmEntityName(),(JpaRepository<?,?>)r));
|
repositoriesByClassName.put(r.getEdmEntityName(),(EdmEntityRepository<?>)r));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public JpaRepository<?, ?> getRepositoryForEntity(EdmEntityType entityType) {
|
public EdmEntityRepository<?> getRepositoryForEntity(EdmEntityType entityType) {
|
||||||
JpaRepository<?, ?> repo = repositoriesByClassName.get(entityType.getName());
|
EdmEntityRepository<?> repo = repositoriesByClassName.get(entityType.getName());
|
||||||
return repo;
|
return repo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
jpa:
|
jpa:
|
||||||
show-sql: true
|
show-sql: true
|
||||||
|
|
Loading…
Reference in New Issue