fix conflicts
This commit is contained in:
commit
6f7ebf8574
|
@ -77,3 +77,4 @@ apache-avro/src/main/java/com/baeldung/avro/model/
|
||||||
jta/transaction-logs/
|
jta/transaction-logs/
|
||||||
software-security/sql-injection-samples/derby.log
|
software-security/sql-injection-samples/derby.log
|
||||||
spring-soap/src/main/java/com/baeldung/springsoap/gen/
|
spring-soap/src/main/java/com/baeldung/springsoap/gen/
|
||||||
|
/report-*.json
|
|
@ -60,7 +60,6 @@
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
<junit.version>4.10</junit.version>
|
|
||||||
<meecrowave-junit.version>1.2.0</meecrowave-junit.version>
|
<meecrowave-junit.version>1.2.0</meecrowave-junit.version>
|
||||||
<okhttp.version>3.10.0</okhttp.version>
|
<okhttp.version>3.10.0</okhttp.version>
|
||||||
<meecrowave-jpa.version>1.2.1</meecrowave-jpa.version>
|
<meecrowave-jpa.version>1.2.1</meecrowave-jpa.version>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
## OData test URLs
|
||||||
|
|
||||||
|
This following table contains test URLs that can be used with the Olingo V2 demo project.
|
||||||
|
|
||||||
|
| URL | Description |
|
||||||
|
|------------------------------------------|-------------------------------------------------|
|
||||||
|
| `http://localhost:8180/odata/$metadata` | fetch OData metadata document |
|
||||||
|
| `http://localhost:8180/odata/CarMakers?$top=10&$skip=10` | Get 10 entities starting at offset 10 |
|
||||||
|
| `http://localhost:8180/odata/CarMakers?$count` | Return total count of entities in this set |
|
||||||
|
| `http://localhost:8180/odata/CarMakers?$filter=startswith(Name,'B')` | Return entities where the *Name* property starts with 'B' |
|
||||||
|
| `http://localhost:8180/odata/CarModels?$filter=Year eq 2008 and CarMakerDetails/Name eq 'BWM'` | Return *CarModel* entities where the *Name* property of its maker starts with 'B' |
|
||||||
|
| `http://localhost:8180/odata/CarModels(1L)?$expand=CarMakerDetails` | Return the *CarModel* with primary key '1', along with its maker|
|
||||||
|
| `http://localhost:8180/odata/CarModels(1L)?$select=Name,Sku` | Return the *CarModel* with primary key '1', returing only its *Name* and *Sku* properties |
|
||||||
|
| `http://localhost:8180/odata/CarModels?$orderBy=Name asc,Sku desc` | Return *CarModel* entities, ordered by the their *Name* and *Sku* properties |
|
||||||
|
| `http://localhost:8180/odata/CarModels?$format=json` | Return *CarModel* entities, using a JSON representation|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
HELP.md
|
||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.1.3.RELEASE</version>
|
||||||
|
<relativePath /> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>org.baeldung.examples.olingo2</groupId>
|
||||||
|
<artifactId>olingo2-sample</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>olingo2-sample</name>
|
||||||
|
<description>Sample Olingo 2 Project</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<olingo2.version>2.0.11</olingo2.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jersey</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Olingo 2 Dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>olingo-odata2-core</artifactId>
|
||||||
|
<version>${olingo2.version}</version>
|
||||||
|
<!-- Avoid jax-rs version conflict by excluding Olingo's version -->
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>javax.ws.rs</groupId>
|
||||||
|
<artifactId>javax.ws.rs-api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>olingo-odata2-api</artifactId>
|
||||||
|
<version>${olingo2.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>olingo-odata2-jpa-processor-api</artifactId>
|
||||||
|
<version>${olingo2.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>olingo-odata2-jpa-processor-core</artifactId>
|
||||||
|
<version>${olingo2.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>olingo-odata2-jpa-processor-ref</artifactId>
|
||||||
|
<version>${olingo2.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.eclipse.persistence</groupId>
|
||||||
|
<artifactId>eclipselink</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,298 @@
|
||||||
|
package org.baeldung.examples.olingo2;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.persistence.EntityGraph;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.EntityTransaction;
|
||||||
|
import javax.persistence.FlushModeType;
|
||||||
|
import javax.persistence.LockModeType;
|
||||||
|
import javax.persistence.Persistence;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import javax.persistence.StoredProcedureQuery;
|
||||||
|
import javax.persistence.SynchronizationType;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaDelete;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaUpdate;
|
||||||
|
import javax.persistence.metamodel.Metamodel;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.olingo.odata2.api.processor.ODataContext;
|
||||||
|
import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext;
|
||||||
|
import org.apache.olingo.odata2.jpa.processor.api.ODataJPAServiceFactory;
|
||||||
|
import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPARuntimeException;
|
||||||
|
import org.baeldung.examples.olingo2.JerseyConfig.EntityManagerFilter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
|
||||||
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ODataJPAServiceFactory implementation for our sample domain
|
||||||
|
* @author Philippe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CarsODataJPAServiceFactory extends ODataJPAServiceFactory {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CarsODataJPAServiceFactory.class);
|
||||||
|
|
||||||
|
public CarsODataJPAServiceFactory() {
|
||||||
|
// Enable detailed error messages (useful for debugging)
|
||||||
|
setDetailErrors(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be called by Olingo on every request to
|
||||||
|
* initialize the ODataJPAContext that will be used.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ODataJPAContext initializeODataJPAContext() throws ODataJPARuntimeException {
|
||||||
|
|
||||||
|
log.info("[I32] >>> initializeODataJPAContext()");
|
||||||
|
ODataJPAContext ctx = getODataJPAContext();
|
||||||
|
ODataContext octx = ctx.getODataContext();
|
||||||
|
HttpServletRequest request = (HttpServletRequest)octx.getParameter(ODataContext.HTTP_SERVLET_REQUEST_OBJECT);
|
||||||
|
EntityManager em = (EntityManager)request.getAttribute(EntityManagerFilter.EM_REQUEST_ATTRIBUTE);
|
||||||
|
|
||||||
|
// Here we're passing the EM that was created by the EntityManagerFilter (see JerseyConfig)
|
||||||
|
ctx.setEntityManager(new EntityManagerWrapper(em));
|
||||||
|
ctx.setPersistenceUnitName("default");
|
||||||
|
|
||||||
|
// We're managing the EM's lifecycle, so we must inform Olingo that it should not
|
||||||
|
// try to manage transactions and/or persistence sessions
|
||||||
|
ctx.setContainerManaged(true);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EntityManagerWrapper implements EntityManager {
|
||||||
|
|
||||||
|
private EntityManager delegate;
|
||||||
|
|
||||||
|
public void persist(Object entity) {
|
||||||
|
log.info("[I68] persist: entity.class=" + entity.getClass()
|
||||||
|
.getSimpleName());
|
||||||
|
delegate.persist(entity);
|
||||||
|
// delegate.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T merge(T entity) {
|
||||||
|
log.info("[I74] merge: entity.class=" + entity.getClass()
|
||||||
|
.getSimpleName());
|
||||||
|
return delegate.merge(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Object entity) {
|
||||||
|
log.info("[I78] remove: entity.class=" + entity.getClass()
|
||||||
|
.getSimpleName());
|
||||||
|
delegate.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T find(Class<T> entityClass, Object primaryKey) {
|
||||||
|
return delegate.find(entityClass, primaryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
|
||||||
|
return delegate.find(entityClass, primaryKey, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
|
||||||
|
return delegate.find(entityClass, primaryKey, lockMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
|
||||||
|
return delegate.find(entityClass, primaryKey, lockMode, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getReference(Class<T> entityClass, Object primaryKey) {
|
||||||
|
return delegate.getReference(entityClass, primaryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
delegate.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlushMode(FlushModeType flushMode) {
|
||||||
|
delegate.setFlushMode(flushMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlushModeType getFlushMode() {
|
||||||
|
return delegate.getFlushMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void lock(Object entity, LockModeType lockMode) {
|
||||||
|
delegate.lock(entity, lockMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {
|
||||||
|
delegate.lock(entity, lockMode, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh(Object entity) {
|
||||||
|
delegate.refresh(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh(Object entity, Map<String, Object> properties) {
|
||||||
|
delegate.refresh(entity, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh(Object entity, LockModeType lockMode) {
|
||||||
|
delegate.refresh(entity, lockMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
|
||||||
|
delegate.refresh(entity, lockMode, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
delegate.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void detach(Object entity) {
|
||||||
|
delegate.detach(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Object entity) {
|
||||||
|
return delegate.contains(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockModeType getLockMode(Object entity) {
|
||||||
|
return delegate.getLockMode(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperty(String propertyName, Object value) {
|
||||||
|
delegate.setProperty(propertyName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getProperties() {
|
||||||
|
return delegate.getProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query createQuery(String qlString) {
|
||||||
|
return delegate.createQuery(qlString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
|
||||||
|
return delegate.createQuery(criteriaQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query createQuery(CriteriaUpdate updateQuery) {
|
||||||
|
return delegate.createQuery(updateQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query createQuery(CriteriaDelete deleteQuery) {
|
||||||
|
return delegate.createQuery(deleteQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
|
||||||
|
return delegate.createQuery(qlString, resultClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query createNamedQuery(String name) {
|
||||||
|
return delegate.createNamedQuery(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
|
||||||
|
return delegate.createNamedQuery(name, resultClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query createNativeQuery(String sqlString) {
|
||||||
|
return delegate.createNativeQuery(sqlString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query createNativeQuery(String sqlString, Class resultClass) {
|
||||||
|
return delegate.createNativeQuery(sqlString, resultClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query createNativeQuery(String sqlString, String resultSetMapping) {
|
||||||
|
return delegate.createNativeQuery(sqlString, resultSetMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
|
||||||
|
return delegate.createNamedStoredProcedureQuery(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
|
||||||
|
return delegate.createStoredProcedureQuery(procedureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
|
||||||
|
return delegate.createStoredProcedureQuery(procedureName, resultClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
|
||||||
|
return delegate.createStoredProcedureQuery(procedureName, resultSetMappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void joinTransaction() {
|
||||||
|
delegate.joinTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJoinedToTransaction() {
|
||||||
|
return delegate.isJoinedToTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T unwrap(Class<T> cls) {
|
||||||
|
return delegate.unwrap(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getDelegate() {
|
||||||
|
return delegate.getDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
log.info("[I229] close");
|
||||||
|
delegate.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOpen() {
|
||||||
|
boolean isOpen = delegate.isOpen();
|
||||||
|
log.info("[I236] isOpen: " + isOpen);
|
||||||
|
return isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityTransaction getTransaction() {
|
||||||
|
log.info("[I240] getTransaction()");
|
||||||
|
return delegate.getTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityManagerFactory getEntityManagerFactory() {
|
||||||
|
return delegate.getEntityManagerFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CriteriaBuilder getCriteriaBuilder() {
|
||||||
|
return delegate.getCriteriaBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Metamodel getMetamodel() {
|
||||||
|
return delegate.getMetamodel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> EntityGraph<T> createEntityGraph(Class<T> rootType) {
|
||||||
|
return delegate.createEntityGraph(rootType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityGraph<?> createEntityGraph(String graphName) {
|
||||||
|
return delegate.createEntityGraph(graphName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityGraph<?> getEntityGraph(String graphName) {
|
||||||
|
return delegate.getEntityGraph(graphName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass) {
|
||||||
|
return delegate.getEntityGraphs(entityClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityManagerWrapper(EntityManager delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package org.baeldung.examples.olingo2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.EntityTransaction;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.ApplicationPath;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerRequestFilter;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseFilter;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
import org.apache.olingo.odata2.api.ODataServiceFactory;
|
||||||
|
import org.apache.olingo.odata2.core.rest.ODataRootLocator;
|
||||||
|
import org.apache.olingo.odata2.core.rest.app.ODataApplication;
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jersey JAX-RS configuration
|
||||||
|
* @author Philippe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ApplicationPath("/odata")
|
||||||
|
public class JerseyConfig extends ResourceConfig {
|
||||||
|
|
||||||
|
|
||||||
|
public JerseyConfig(CarsODataJPAServiceFactory serviceFactory, EntityManagerFactory emf) {
|
||||||
|
|
||||||
|
ODataApplication app = new ODataApplication();
|
||||||
|
|
||||||
|
app
|
||||||
|
.getClasses()
|
||||||
|
.forEach( c -> {
|
||||||
|
// Avoid using the default RootLocator, as we want
|
||||||
|
// a Spring Managed one
|
||||||
|
if ( !ODataRootLocator.class.isAssignableFrom(c)) {
|
||||||
|
register(c);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
register(new CarsRootLocator(serviceFactory));
|
||||||
|
register( new EntityManagerFilter(emf));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This filter handles the EntityManager transaction lifecycle.
|
||||||
|
* @author Philippe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Provider
|
||||||
|
public static class EntityManagerFilter implements ContainerRequestFilter, ContainerResponseFilter {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(EntityManagerFilter.class);
|
||||||
|
public static final String EM_REQUEST_ATTRIBUTE = EntityManagerFilter.class.getName() + "_ENTITY_MANAGER";
|
||||||
|
|
||||||
|
private final EntityManagerFactory emf;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private HttpServletRequest httpRequest;
|
||||||
|
|
||||||
|
public EntityManagerFilter(EntityManagerFactory emf) {
|
||||||
|
this.emf = emf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext ctx) throws IOException {
|
||||||
|
log.info("[I60] >>> filter");
|
||||||
|
EntityManager em = this.emf.createEntityManager();
|
||||||
|
httpRequest.setAttribute(EM_REQUEST_ATTRIBUTE, em);
|
||||||
|
|
||||||
|
// Start a new transaction unless we have a simple GET
|
||||||
|
if (!"GET".equalsIgnoreCase(ctx.getMethod())) {
|
||||||
|
em.getTransaction()
|
||||||
|
.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
|
||||||
|
|
||||||
|
log.info("[I68] <<< filter");
|
||||||
|
EntityManager em = (EntityManager) httpRequest.getAttribute(EM_REQUEST_ATTRIBUTE);
|
||||||
|
|
||||||
|
if (!"GET".equalsIgnoreCase(requestContext.getMethod())) {
|
||||||
|
EntityTransaction t = em.getTransaction();
|
||||||
|
if (t.isActive()) {
|
||||||
|
if (!t.getRollbackOnly()) {
|
||||||
|
t.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
em.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
public static class CarsRootLocator extends ODataRootLocator {
|
||||||
|
|
||||||
|
private CarsODataJPAServiceFactory serviceFactory;
|
||||||
|
|
||||||
|
public CarsRootLocator(CarsODataJPAServiceFactory serviceFactory) {
|
||||||
|
this.serviceFactory = serviceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ODataServiceFactory getServiceFactory() {
|
||||||
|
return this.serviceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.baeldung.examples.olingo2;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Olingo2SampleApplication extends SpringBootServletInitializer {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Olingo2SampleApplication.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.baeldung.examples.olingo2.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@Table(name="car_maker")
|
||||||
|
public class CarMaker {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy=GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(name="name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy="maker",
|
||||||
|
orphanRemoval = true,
|
||||||
|
cascade=CascadeType.ALL)
|
||||||
|
private List<CarModel> models;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.baeldung.examples.olingo2.domain;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@Table(name="car_model")
|
||||||
|
public class CarModel {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy=GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Integer year;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String sku;
|
||||||
|
|
||||||
|
@ManyToOne(optional=false, fetch= FetchType.LAZY)
|
||||||
|
@JoinColumn(name="maker_fk")
|
||||||
|
private CarMaker maker;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
server:
|
||||||
|
port: 8180
|
||||||
|
|
||||||
|
spring:
|
||||||
|
jersey:
|
||||||
|
application-path: /odata
|
||||||
|
|
||||||
|
jpa:
|
||||||
|
show-sql: true
|
||||||
|
open-in-view: false
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
|
@ -0,0 +1,12 @@
|
||||||
|
insert into car_maker(id,name) values (1,'Special Motors');
|
||||||
|
insert into car_maker(id,name) values (2,'BWM');
|
||||||
|
insert into car_maker(id,name) values (3,'Dolores');
|
||||||
|
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(1,1,'Muze','SM001',2018);
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(2,1,'Empada','SM002',2008);
|
||||||
|
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(4,2,'BWM-100','BWM100',2008);
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(5,2,'BWM-200','BWM200',2009);
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(6,2,'BWM-300','BWM300',2008);
|
||||||
|
|
||||||
|
alter sequence hibernate_sequence restart with 100;
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.baeldung.examples.olingo2;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest
|
||||||
|
public class Olingo2SampleApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,256 @@
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"_postman_id": "afa8e1e5-ab0e-4f1d-8b99-b7d1f091f975",
|
||||||
|
"name": "OLingo2 - Cars",
|
||||||
|
"schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
|
||||||
|
},
|
||||||
|
"item": [
|
||||||
|
{
|
||||||
|
"name": "GET Metadata",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": "http://localhost:8080/odata/$metadata"
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GET All CarMakers",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": "http://localhost:8080/odata/CarMakers"
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GET Makers with Pagination",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "http://localhost:8080/odata/CarMakers?$top=1&$orderby=Name&$skip=3",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "8080",
|
||||||
|
"path": [
|
||||||
|
"odata",
|
||||||
|
"CarMakers"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "$top",
|
||||||
|
"value": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "$orderby",
|
||||||
|
"value": "Name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "$skip",
|
||||||
|
"value": "3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GET Makers and Models",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "http://localhost:8080/odata/CarMakers?$expand=CarModelDetails",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "8080",
|
||||||
|
"path": [
|
||||||
|
"odata",
|
||||||
|
"CarMakers"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "$expand",
|
||||||
|
"value": "CarModelDetails"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GET Makers with filter",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"value": "application/json",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "http://localhost:8080/odata/CarMakers?$filter=Name eq 'BWM'&$expand=CarModelDetails",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "8080",
|
||||||
|
"path": [
|
||||||
|
"odata",
|
||||||
|
"CarMakers"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "$filter",
|
||||||
|
"value": "Name eq 'BWM'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "$expand",
|
||||||
|
"value": "CarModelDetails"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Create CarMaker",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/atom+xml",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"value": "application/atom+xml",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \r\n<entry xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\"\r\n xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\" \r\n xmlns=\"http://www.w3.org/2005/Atom\">\r\n <title type=\"text\"></title> \r\n <updated>2019-04-02T21:36:47Z</updated>\r\n <author> \r\n <name /> \r\n </author> \r\n <category term=\"default.CarMaker\"\r\n scheme=\"http://schemas.microsoft.com/ado/2007/08/dataservices/scheme\" /> \r\n <content type=\"application/xml\"> \r\n <m:properties>\r\n <d:Name>Lucien</d:Name>\r\n </m:properties> \r\n </content> \r\n</entry>"
|
||||||
|
},
|
||||||
|
"url": "http://localhost:8080/odata/CarMakers"
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Create CarModel",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"name": "Content-Type",
|
||||||
|
"type": "text",
|
||||||
|
"value": "application/atom+xml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"type": "text",
|
||||||
|
"value": "application/atom+xml"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \r\n<entry xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\"\r\n xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\" \r\n xmlns=\"http://www.w3.org/2005/Atom\">\r\n <title type=\"text\"></title> \r\n <updated>2019-04-02T21:36:47Z</updated>\r\n <author> \r\n <name /> \r\n </author> \r\n <link href=\"CarModels(1L)/CarMakerDetails\" \r\n rel=\"http://schemas.microsoft.com/ado/2007/08/dataservices/related/CarMakerDetails\" \r\n title=\"CarMakerDetails\" \r\n type=\"application/atom+xml;type=entry\"></link> \r\n <category term=\"default.CarModel\"\r\n scheme=\"http://schemas.microsoft.com/ado/2007/08/dataservices/scheme\" /> \r\n <content type=\"application/xml\"> \r\n <m:properties>\r\n\t <d:Name>Tata</d:Name>\r\n\t <d:Sku>TT101</d:Sku>\r\n\t <d:Year>2018</d:Year>\r\n </m:properties> \r\n </content> \r\n</entry>"
|
||||||
|
},
|
||||||
|
"url": "http://localhost:8080/odata/CarModels"
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Update CarMaker",
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"name": "Content-Type",
|
||||||
|
"type": "text",
|
||||||
|
"value": "application/atom+xml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"type": "text",
|
||||||
|
"value": "application/atom+xml"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \r\n<entry xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\"\r\n xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\" \r\n xmlns=\"http://www.w3.org/2005/Atom\">\r\n <title type=\"text\"></title> \r\n <updated>2019-04-02T21:36:47Z</updated>\r\n <author> \r\n <name /> \r\n </author> \r\n <category term=\"default.CarMaker\"\r\n scheme=\"http://schemas.microsoft.com/ado/2007/08/dataservices/scheme\" /> \r\n <content type=\"application/xml\"> \r\n <m:properties>\r\n <d:Id>5</d:Id>\r\n <d:Name>KaiserWagen</d:Name>\r\n </m:properties> \r\n </content> \r\n</entry>"
|
||||||
|
},
|
||||||
|
"url": "http://localhost:8080/odata/CarMakers(5L)"
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "All CarModels",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": "http://localhost:8080/odata/CarModels"
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Delete CarModel",
|
||||||
|
"request": {
|
||||||
|
"method": "DELETE",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"name": "Content-Type",
|
||||||
|
"type": "text",
|
||||||
|
"value": "application/atom+xml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"type": "text",
|
||||||
|
"value": "application/atom+xml"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": ""
|
||||||
|
},
|
||||||
|
"url": "http://localhost:8080/odata/CarModels(100L)"
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
HELP.md
|
||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.1.3.RELEASE</version>
|
||||||
|
<relativePath /> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>org.baeldung.examples.olingo4</groupId>
|
||||||
|
<artifactId>olingo4-sample</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>olingo4-sample</name>
|
||||||
|
<description>Sample Olingo 4 Project</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<odata.version>4.5.0</odata.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>odata-server-api</artifactId>
|
||||||
|
<version>${odata.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>odata-server-core</artifactId>
|
||||||
|
<version>${odata.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>odata-commons-api</artifactId>
|
||||||
|
<version>${odata.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.olingo</groupId>
|
||||||
|
<artifactId>odata-commons-core</artifactId>
|
||||||
|
<version>${odata.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-beanutils</groupId>
|
||||||
|
<artifactId>commons-beanutils</artifactId>
|
||||||
|
<version>1.9.3</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import org.apache.olingo.server.api.OData;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation for ODataFactory
|
||||||
|
* @author Philippe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DefaultODataFactory implements ODataFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OData newInstance() {
|
||||||
|
return OData.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import org.apache.olingo.server.api.OData;
|
||||||
|
|
||||||
|
public interface ODataFactory {
|
||||||
|
|
||||||
|
public OData newInstance();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import org.apache.olingo.server.api.ODataHttpHandler;
|
||||||
|
|
||||||
|
public interface ODataHttpHandlerFactory {
|
||||||
|
|
||||||
|
ODataHttpHandler newInstance();
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider;
|
||||||
|
import org.apache.olingo.server.api.OData;
|
||||||
|
import org.apache.olingo.server.api.ODataHttpHandler;
|
||||||
|
import org.apache.olingo.server.api.ServiceMetadata;
|
||||||
|
import org.apache.olingo.server.api.processor.Processor;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public class ODataHttpHandlerFactoryImpl implements ODataHttpHandlerFactory {
|
||||||
|
|
||||||
|
|
||||||
|
private final ODataFactory odataFactory;
|
||||||
|
private final CsdlEdmProvider edmProvider;
|
||||||
|
private final List<Processor> processors;
|
||||||
|
|
||||||
|
public ODataHttpHandlerFactoryImpl(ODataFactory odataFactory,CsdlEdmProvider edmProvider, List<Processor> processors) {
|
||||||
|
this.odataFactory = odataFactory;
|
||||||
|
this.edmProvider = edmProvider;
|
||||||
|
this.processors = processors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ODataHttpHandler newInstance() {
|
||||||
|
|
||||||
|
OData odata = odataFactory.newInstance();
|
||||||
|
ServiceMetadata metadata = odata.createServiceMetadata(edmProvider, Collections.emptyList());
|
||||||
|
ODataHttpHandler handler = odata.createHandler(metadata);
|
||||||
|
|
||||||
|
// Register all available processors
|
||||||
|
processors.forEach(p -> handler.register(p));
|
||||||
|
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider;
|
||||||
|
import org.apache.olingo.server.api.processor.Processor;
|
||||||
|
import org.baeldung.examples.olingo4.ODataHttpHandlerFactoryImpl.ODataHttpHandlerFactoryImplBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class ODataServiceConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServletRegistrationBean<HttpServlet> odataServletRegistration(ODataHttpHandlerFactory factory) {
|
||||||
|
ServletRegistrationBean<HttpServlet> srb =
|
||||||
|
new ServletRegistrationBean<>(new ODataServlet(factory), "/odata/*");
|
||||||
|
srb.setLoadOnStartup(1);
|
||||||
|
return srb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ODataHttpHandlerFactory httpHandlerFactory(CsdlEdmProvider edmProvider, ODataFactory odataFactory, List<Processor> processors) {
|
||||||
|
return new ODataHttpHandlerFactoryImplBuilder()
|
||||||
|
.edmProvider(edmProvider)
|
||||||
|
.odataFactory(odataFactory)
|
||||||
|
.processors(processors)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.EntityTransaction;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.olingo.server.api.ODataHttpHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Philippe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ODataServlet extends HttpServlet {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final ODataHttpHandlerFactory odataHttpHandlerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
public ODataServlet(ODataHttpHandlerFactory factory) {
|
||||||
|
this.odataHttpHandlerFactory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
|
||||||
|
ODataHttpHandler handler = odataHttpHandlerFactory.newInstance();
|
||||||
|
handler.process(req, resp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Olingo4SampleApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Olingo4SampleApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.baeldung.examples.olingo4.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@Table(name="car_maker")
|
||||||
|
public class CarMaker {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy=GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(name="name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy="maker",
|
||||||
|
orphanRemoval = true,
|
||||||
|
cascade=CascadeType.ALL)
|
||||||
|
private List<CarModel> models;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.baeldung.examples.olingo4.domain;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@Table(name="car_model")
|
||||||
|
public class CarModel {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy=GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Integer year;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String sku;
|
||||||
|
|
||||||
|
@ManyToOne(optional=false, fetch= FetchType.EAGER )
|
||||||
|
@JoinColumn(name="maker_fk")
|
||||||
|
private CarMaker maker;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.baeldung.examples.olingo4.edm;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.sql.Time;
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
|
|
||||||
|
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class EdmTypeMapper {
|
||||||
|
|
||||||
|
public EdmPrimitiveTypeKind java2edm(Class<?> clazz) {
|
||||||
|
EdmPrimitiveTypeKind result = java2edm.get(clazz);
|
||||||
|
if ( result == null ) {
|
||||||
|
throw new IllegalArgumentException("[E19] Unsupported class mapping: class=" + clazz);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static map used generate attribute metadada based on Java types
|
||||||
|
static Map<Class<?>,EdmPrimitiveTypeKind> java2edm = Collections
|
||||||
|
.unmodifiableMap(Stream.of(
|
||||||
|
new SimpleEntry<>(Boolean.class,EdmPrimitiveTypeKind.Boolean),
|
||||||
|
new SimpleEntry<>(Byte.class,EdmPrimitiveTypeKind.SByte),
|
||||||
|
new SimpleEntry<>(Date.class,EdmPrimitiveTypeKind.Date),
|
||||||
|
new SimpleEntry<>(Time.class,EdmPrimitiveTypeKind.TimeOfDay),
|
||||||
|
new SimpleEntry<>(Number.class,EdmPrimitiveTypeKind.Decimal),
|
||||||
|
new SimpleEntry<>(Float.class,EdmPrimitiveTypeKind.Single),
|
||||||
|
new SimpleEntry<>(Double.class,EdmPrimitiveTypeKind.Double),
|
||||||
|
new SimpleEntry<>(UUID.class,EdmPrimitiveTypeKind.Guid),
|
||||||
|
new SimpleEntry<>(Short.class,EdmPrimitiveTypeKind.Int16),
|
||||||
|
new SimpleEntry<>(Integer.class,EdmPrimitiveTypeKind.Int32),
|
||||||
|
new SimpleEntry<>(Long.class,EdmPrimitiveTypeKind.Int64),
|
||||||
|
new SimpleEntry<>(String.class,EdmPrimitiveTypeKind.String)
|
||||||
|
|
||||||
|
).collect(Collectors.toMap((e)-> e.getKey(),(e)-> e.getValue())));
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,269 @@
|
||||||
|
package org.baeldung.examples.olingo4.edm;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.metamodel.Attribute.PersistentAttributeType;
|
||||||
|
import javax.persistence.metamodel.EntityType;
|
||||||
|
import javax.persistence.metamodel.Metamodel;
|
||||||
|
import javax.persistence.metamodel.PluralAttribute;
|
||||||
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainerInfo;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlEntityType;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlProperty;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlPropertyRef;
|
||||||
|
import org.apache.olingo.commons.api.edm.provider.CsdlSchema;
|
||||||
|
import org.apache.olingo.commons.api.ex.ODataException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JpaEdmProvider extends CsdlAbstractEdmProvider {
|
||||||
|
|
||||||
|
EntityManagerFactory emf;
|
||||||
|
|
||||||
|
//
|
||||||
|
private EdmTypeMapper typeMapper;
|
||||||
|
|
||||||
|
// Service Namespace
|
||||||
|
public static final String NAMESPACE = "Baeldung.OData";
|
||||||
|
|
||||||
|
// EDM Container
|
||||||
|
public static final String CONTAINER_NAME = "Cars";
|
||||||
|
public static final FullQualifiedName CONTAINER = new FullQualifiedName(NAMESPACE, CONTAINER_NAME);
|
||||||
|
|
||||||
|
// Caches of OData types by it fully qualified name
|
||||||
|
private Map<FullQualifiedName, CsdlEntityType> cdslName2Type = new HashMap<>();
|
||||||
|
|
||||||
|
public JpaEdmProvider(EntityManagerFactory emf, EdmTypeMapper mapper) {
|
||||||
|
this.emf = emf;
|
||||||
|
this.typeMapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider#getEntitySet(org.apache.olingo.commons.api.edm.FullQualifiedName, java.lang.String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CsdlEntitySet getEntitySet(FullQualifiedName entityContainer, String entitySetName) throws ODataException {
|
||||||
|
|
||||||
|
if (entityContainer.equals(CONTAINER)) {
|
||||||
|
|
||||||
|
EntityType<?> e = emf.getMetamodel()
|
||||||
|
.getEntities()
|
||||||
|
.stream()
|
||||||
|
.filter((ent) -> (ent.getName() + "s")
|
||||||
|
.equals(entitySetName))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (e != null) {
|
||||||
|
CsdlEntitySet entitySet = new CsdlEntitySet();
|
||||||
|
entitySet
|
||||||
|
.setName(entitySetName)
|
||||||
|
.setType(new FullQualifiedName(NAMESPACE, e.getName()));
|
||||||
|
return entitySet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider#getEntityContainerInfo(org.apache.olingo.commons.api.edm.FullQualifiedName)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CsdlEntityContainerInfo getEntityContainerInfo(FullQualifiedName entityContainerName) throws ODataException {
|
||||||
|
|
||||||
|
// This method is invoked when displaying the Service Document at e.g. http://localhost:8080/DemoService/DemoService.svc
|
||||||
|
if (entityContainerName == null || entityContainerName.equals(CONTAINER)) {
|
||||||
|
CsdlEntityContainerInfo entityContainerInfo = new CsdlEntityContainerInfo();
|
||||||
|
entityContainerInfo.setContainerName(CONTAINER);
|
||||||
|
return entityContainerInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider#getSchemas()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<CsdlSchema> getSchemas() throws ODataException {
|
||||||
|
// create Schema
|
||||||
|
CsdlSchema schema = new CsdlSchema();
|
||||||
|
schema.setNamespace(NAMESPACE);
|
||||||
|
|
||||||
|
// add EntityTypes
|
||||||
|
List<CsdlEntityType> entityTypes = emf.getMetamodel()
|
||||||
|
.getEntities()
|
||||||
|
.stream()
|
||||||
|
.map((e) -> {
|
||||||
|
try {
|
||||||
|
return getEntityType(new FullQualifiedName(NAMESPACE, e.getName()));
|
||||||
|
} catch (ODataException oe) {
|
||||||
|
throw new RuntimeException(oe);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
schema.setEntityTypes(entityTypes);
|
||||||
|
|
||||||
|
// add EntityContainer
|
||||||
|
schema.setEntityContainer(getEntityContainer());
|
||||||
|
|
||||||
|
// finally
|
||||||
|
List<CsdlSchema> schemas = new ArrayList<CsdlSchema>();
|
||||||
|
schemas.add(schema);
|
||||||
|
|
||||||
|
return schemas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider#getEntityContainer()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CsdlEntityContainer getEntityContainer() throws ODataException {
|
||||||
|
|
||||||
|
|
||||||
|
// add EntityTypes
|
||||||
|
List<CsdlEntitySet> entitySets = emf.getMetamodel()
|
||||||
|
.getEntities()
|
||||||
|
.stream()
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// create EntityContainer
|
||||||
|
CsdlEntityContainer entityContainer = new CsdlEntityContainer();
|
||||||
|
entityContainer.setName(CONTAINER_NAME);
|
||||||
|
entityContainer.setEntitySets(entitySets);
|
||||||
|
|
||||||
|
return entityContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) throws ODataException {
|
||||||
|
|
||||||
|
CsdlEntityType result = cdslName2Type.get(entityTypeName);
|
||||||
|
if ( result != null ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Metamodel mm = emf.getMetamodel();
|
||||||
|
result = mm.getEntities()
|
||||||
|
.stream()
|
||||||
|
.filter(et -> entityTypeName.equals(new FullQualifiedName(NAMESPACE, et.getName())))
|
||||||
|
.map(et -> buildODataType(et))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
// save for future use
|
||||||
|
cdslName2Type.put(entityTypeName, result);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a JPA type to its OData counterpart.
|
||||||
|
* @param et
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected CsdlEntityType buildODataType(EntityType<?> et) {
|
||||||
|
|
||||||
|
CsdlEntityType result = new CsdlEntityType();
|
||||||
|
result.setName(et.getName());
|
||||||
|
|
||||||
|
// Process simple properties
|
||||||
|
List<CsdlProperty> properties = et.getDeclaredSingularAttributes()
|
||||||
|
.stream()
|
||||||
|
.filter(attr -> attr.getPersistentAttributeType() == PersistentAttributeType.BASIC)
|
||||||
|
.map(attr -> buildBasicAttribute(et, attr))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
result.setProperties(properties);
|
||||||
|
|
||||||
|
// Process Ids
|
||||||
|
List<CsdlPropertyRef> ids = et.getDeclaredSingularAttributes()
|
||||||
|
.stream()
|
||||||
|
.filter(attr -> attr.getPersistentAttributeType() == PersistentAttributeType.BASIC && attr.isId())
|
||||||
|
.map(attr -> buildRefAttribute(et, attr))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
result.setKey(ids);
|
||||||
|
|
||||||
|
// Process 1:N navs
|
||||||
|
List<CsdlNavigationProperty> navs = et.getDeclaredPluralAttributes()
|
||||||
|
.stream()
|
||||||
|
.map(attr -> buildNavAttribute(et, attr))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsdlProperty buildBasicAttribute(EntityType<?> et, SingularAttribute<?, ?> attr) {
|
||||||
|
|
||||||
|
CsdlProperty p = new CsdlProperty().setName(attr.getName())
|
||||||
|
.setType(typeMapper.java2edm(attr.getJavaType())
|
||||||
|
.getFullQualifiedName())
|
||||||
|
.setNullable(et.getDeclaredSingularAttribute(attr.getName())
|
||||||
|
.isOptional());
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsdlPropertyRef buildRefAttribute(EntityType<?> et, SingularAttribute<?, ?> attr) {
|
||||||
|
|
||||||
|
CsdlPropertyRef p = new CsdlPropertyRef().setName(attr.getName());
|
||||||
|
|
||||||
|
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()))
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
package org.baeldung.examples.olingo4.processor;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.metamodel.EntityType;
|
||||||
|
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.data.Property;
|
||||||
|
import org.apache.olingo.commons.api.data.ValueType;
|
||||||
|
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.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.CountEntityCollectionProcessor;
|
||||||
|
import org.apache.olingo.server.api.processor.EntityCollectionProcessor;
|
||||||
|
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
|
||||||
|
import org.apache.olingo.server.api.serializer.ODataSerializer;
|
||||||
|
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.baeldung.examples.olingo4.repository.RepositoryRegistry;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JpaEntityCollectionProcessor implements CountEntityCollectionProcessor {
|
||||||
|
|
||||||
|
private OData odata;
|
||||||
|
private ServiceMetadata serviceMetadata;
|
||||||
|
private EntityManagerFactory emf;
|
||||||
|
private RepositoryRegistry repositoryRegistry;
|
||||||
|
private JpaEntityMapper entityMapper;
|
||||||
|
|
||||||
|
public JpaEntityCollectionProcessor(EntityManagerFactory emf, RepositoryRegistry repositoryRegistry, JpaEntityMapper entityMapper) {
|
||||||
|
this.emf = emf;
|
||||||
|
this.repositoryRegistry = repositoryRegistry;
|
||||||
|
this.entityMapper = entityMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(OData odata, ServiceMetadata serviceMetadata) {
|
||||||
|
this.odata = odata;
|
||||||
|
this.serviceMetadata = serviceMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
|
||||||
|
|
||||||
|
// 1st we have retrieve the requested EntitySet from the uriInfo object (representation of the parsed service URI)
|
||||||
|
List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
|
||||||
|
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); // in our example, the first segment is the EntitySet
|
||||||
|
EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
|
||||||
|
|
||||||
|
// 2nd: fetch the data from backend for this requested EntitySetName
|
||||||
|
// it has to be delivered as EntitySet object
|
||||||
|
EntityCollection entitySet = getData(edmEntitySet, uriInfo);
|
||||||
|
|
||||||
|
// 3rd: create a serializer based on the requested format (json)
|
||||||
|
ODataSerializer serializer = odata.createSerializer(responseFormat);
|
||||||
|
|
||||||
|
// 4th: Now serialize the content: transform from the EntitySet object to InputStream
|
||||||
|
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
|
||||||
|
ContextURL contextUrl = ContextURL.with()
|
||||||
|
.entitySet(edmEntitySet)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final String id = request.getRawBaseUri() + "/" + edmEntitySet.getName();
|
||||||
|
EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with()
|
||||||
|
.id(id)
|
||||||
|
.contextURL(contextUrl)
|
||||||
|
.build();
|
||||||
|
SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType, entitySet, opts);
|
||||||
|
InputStream serializedContent = serializerResult.getContent();
|
||||||
|
|
||||||
|
// Finally: configure the response object: set the body, headers and status code
|
||||||
|
response.setContent(serializedContent);
|
||||||
|
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
|
||||||
|
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void countEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException, ODataLibraryException {
|
||||||
|
|
||||||
|
// 1st we have retrieve the requested EntitySet from the uriInfo object (representation of the parsed service URI)
|
||||||
|
List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
|
||||||
|
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); // in our example, the first segment is the EntitySet
|
||||||
|
EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
|
||||||
|
|
||||||
|
// 2nd: fetch the data from backend for this requested EntitySetName
|
||||||
|
Long count = getCount(edmEntitySet, uriInfo);
|
||||||
|
|
||||||
|
// Finally: configure the response object: set the body, headers and status code
|
||||||
|
response.setContent(new ByteArrayInputStream(count.toString().getBytes()));
|
||||||
|
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
|
||||||
|
response.setHeader(HttpHeader.CONTENT_TYPE, "text/plain");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = (JpaRepository<?, ?>)repositoryRegistry.getRepositoryForEntity(type);
|
||||||
|
EntityCollection result = new EntityCollection();
|
||||||
|
|
||||||
|
repo.findAll()
|
||||||
|
.stream()
|
||||||
|
.forEach((it) -> result.getEntities()
|
||||||
|
.add(entityMapper.map2entity(edmEntitySet, it)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to get the total size of an entity set
|
||||||
|
* @param edmEntitySet
|
||||||
|
* @param uriInfo
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Long getCount(EdmEntitySet edmEntitySet, UriInfo uriInfo) {
|
||||||
|
|
||||||
|
EdmEntityType type = edmEntitySet.getEntityType();
|
||||||
|
JpaRepository<?, ?> repo = (JpaRepository<?, ?>)repositoryRegistry.getRepositoryForEntity(type);
|
||||||
|
EntityCollection result = new EntityCollection();
|
||||||
|
|
||||||
|
return repo.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPropertyValue(Object entry, String name,Object value) {
|
||||||
|
try {
|
||||||
|
PropertyUtils.setProperty(entry,name,value);
|
||||||
|
} 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,304 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.baeldung.examples.olingo4.processor;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
import org.apache.olingo.commons.api.data.ContextURL;
|
||||||
|
import org.apache.olingo.commons.api.data.Entity;
|
||||||
|
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.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.EntitySerializerOptions;
|
||||||
|
import org.apache.olingo.server.api.serializer.ODataSerializer;
|
||||||
|
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.repository.JpaRepository;
|
||||||
|
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();
|
||||||
|
|
||||||
|
if ( resourceParts.size() == 1 ) {
|
||||||
|
entityStream = readRootEntity(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;
|
||||||
|
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();
|
||||||
|
//}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
private InputStream readRootEntity(EdmEntitySet entitySet, List<UriParameter> keyPredicates,ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
|
||||||
|
EdmEntityType type = entitySet.getEntityType();
|
||||||
|
JpaRepository repo = registry.getRepositoryForEntity(type);
|
||||||
|
|
||||||
|
// Get key value
|
||||||
|
Long keyValue = getEntityKey(keyPredicates);
|
||||||
|
Optional<Object> entry = repo.findById(keyValue);
|
||||||
|
if ( !entry.isPresent()) {
|
||||||
|
throw new ODataApplicationException(
|
||||||
|
"[E116] NO entity found for the given key",
|
||||||
|
HttpStatusCode.NOT_FOUND.getStatusCode(),
|
||||||
|
Locale.ENGLISH);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity e = entityMapper.map2entity(entitySet, entry.get());
|
||||||
|
return serializeEntity(entitySet,e,responseFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream serializeEntity(EdmEntitySet entitySet, Entity entity,ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
|
||||||
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// @SuppressWarnings("unchecked")
|
||||||
|
// protected InputStream readRelatedEntities(EdmEntitySet rootEntitySet, List<UriParameter> rootPredicates, EdmNavigationProperty property, ContentType responseFormat) throws ODataApplicationException {
|
||||||
|
//
|
||||||
|
// Object jpaEntity = readJPAEntity(rootEntitySet, rootPredicates);
|
||||||
|
// try {
|
||||||
|
// Collection<Object> set = (Collection<Object>)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();
|
||||||
|
//
|
||||||
|
// 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", "unchecked" })
|
||||||
|
private InputStream readRelatedEntity(ODataRequest request, EdmEntitySet entitySet, List<UriParameter> rootPredicates, EdmNavigationProperty property, List<UriParameter> parentPredicates, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
|
||||||
|
|
||||||
|
|
||||||
|
JpaRepository<Object,Object> repo = (JpaRepository<Object,Object>)registry.getRepositoryForEntity(entitySet.getEntityType());
|
||||||
|
EdmEntityRepository<Object> relatedRepo = (EdmEntityRepository<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)repo).getEntityClass();
|
||||||
|
Class<?> relatedClass = ((EdmEntityRepository)relatedRepo).getEntityClass();
|
||||||
|
|
||||||
|
SingularAttribute fk = emf.getMetamodel()
|
||||||
|
.entity(rootClass)
|
||||||
|
.getSingularAttributes()
|
||||||
|
.stream()
|
||||||
|
.filter((attr) -> {
|
||||||
|
boolean b = attr.isAssociation() && attr.getJavaType().isAssignableFrom(relatedClass);
|
||||||
|
return b;
|
||||||
|
})
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if ( fk == null ) {
|
||||||
|
throw new ODataRuntimeException("[E230] No singular attribute of child class '" + relatedClass.getName() + "' found" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Long pkValue = getEntityKey(rootPredicates);
|
||||||
|
EntityManager em = this.emf.createEntityManager();
|
||||||
|
try {
|
||||||
|
// Read data from DB
|
||||||
|
Object root = em.find(rootClass, pkValue);
|
||||||
|
Object related = this.entityMapper.getPropertyValue(root, fk.getName());
|
||||||
|
|
||||||
|
EdmEntitySet relatedEntitySet = entitySetFromType(property.getType());
|
||||||
|
Entity e = entityMapper.map2entity(relatedEntitySet, related);
|
||||||
|
return serializeEntity(relatedEntitySet,e,responseFormat);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @SuppressWarnings("unchecked")
|
||||||
|
// 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 Long getEntityKey(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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.baeldung.examples.olingo4.repository;
|
||||||
|
|
||||||
|
import org.baeldung.examples.olingo4.domain.CarMaker;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CarMakerRepository extends EdmEntityRepository<CarMaker>, JpaRepository<CarMaker, Long>, JpaSpecificationExecutor<CarMaker> {
|
||||||
|
|
||||||
|
public default String getEdmEntityName() { return CarMaker.class.getSimpleName();}
|
||||||
|
@Override
|
||||||
|
default Class<CarMaker> getEntityClass() {
|
||||||
|
return CarMaker.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +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 EdmEntityRepository<CarModel>, JpaRepository<CarModel, Long>, JpaSpecificationExecutor<CarModel> {
|
||||||
|
|
||||||
|
public List<CarModel> findByMakerId(Long makerId);
|
||||||
|
|
||||||
|
public default String getEdmEntityName() { return CarModel.class.getSimpleName();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Class<CarModel> getEntityClass() {
|
||||||
|
return CarModel.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.baeldung.examples.olingo4.repository;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Philippe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface EdmEntityRepository<E> {
|
||||||
|
|
||||||
|
public String getEdmEntityName();
|
||||||
|
public Class<E> getEntityClass();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.baeldung.examples.olingo4.repository;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
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<String,JpaRepository<?,?>> repositoriesByClassName = new HashMap<>();
|
||||||
|
|
||||||
|
public RepositoryRegistry(List<EdmEntityRepository<?>> allRepositories) {
|
||||||
|
|
||||||
|
allRepositories.stream()
|
||||||
|
.forEach((r) ->
|
||||||
|
repositoriesByClassName.put(r.getEdmEntityName(),(JpaRepository<?,?>)r));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JpaRepository<?,?> getRepositoryForEntity(EdmEntityType entityType) {
|
||||||
|
JpaRepository<?,?> repo = repositoriesByClassName.get(entityType.getName());
|
||||||
|
return repo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
spring:
|
||||||
|
jpa:
|
||||||
|
show-sql: true
|
||||||
|
open-in-view: true
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
|
@ -0,0 +1,12 @@
|
||||||
|
insert into car_maker(id,name) values (1,'Special Motors');
|
||||||
|
insert into car_maker(id,name) values (2,'BWM');
|
||||||
|
insert into car_maker(id,name) values (3,'Dolores');
|
||||||
|
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(1,1,'Muze','SM001',2018);
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(2,1,'Empada','SM002',2008);
|
||||||
|
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(4,2,'BWM-100','BWM100',2008);
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(5,2,'BWM-200','BWM200',2009);
|
||||||
|
insert into car_model(id,maker_fk,name,sku,year) values(6,2,'BWM-300','BWM300',2008);
|
||||||
|
|
||||||
|
alter sequence hibernate_sequence restart with 100;
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.baeldung.examples.olingo4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest
|
||||||
|
public class Olingo4SampleApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -59,7 +59,6 @@
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<jstl.version>1.2</jstl.version>
|
|
||||||
<org.apache.httpcomponents.version>4.5.2</org.apache.httpcomponents.version>
|
<org.apache.httpcomponents.version>4.5.2</org.apache.httpcomponents.version>
|
||||||
<velocity-version>1.7</velocity-version>
|
<velocity-version>1.7</velocity-version>
|
||||||
<velocity-tools-version>2.0</velocity-tools-version>
|
<velocity-tools-version>2.0</velocity-tools-version>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hamcrest</groupId>
|
<groupId>org.hamcrest</groupId>
|
||||||
<artifactId>hamcrest-core</artifactId>
|
<artifactId>hamcrest-core</artifactId>
|
||||||
<version>${hamcrest-core.version}</version>
|
<version>${org.hamcrest.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -64,7 +64,6 @@
|
||||||
<cdi-api.version>2.0.SP1</cdi-api.version>
|
<cdi-api.version>2.0.SP1</cdi-api.version>
|
||||||
<weld-se-core.version>3.0.5.Final</weld-se-core.version>
|
<weld-se-core.version>3.0.5.Final</weld-se-core.version>
|
||||||
<aspectjweaver.version>1.9.2</aspectjweaver.version>
|
<aspectjweaver.version>1.9.2</aspectjweaver.version>
|
||||||
<hamcrest-core.version>1.3</hamcrest-core.version>
|
|
||||||
<assertj-core.version>3.10.0</assertj-core.version>
|
<assertj-core.version>3.10.0</assertj-core.version>
|
||||||
<spring.version>5.1.2.RELEASE</spring.version>
|
<spring.version>5.1.2.RELEASE</spring.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
|
@ -35,7 +35,4 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<java.version>1.8</java.version>
|
|
||||||
</properties>
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -35,8 +35,4 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<java.version>1.8</java.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/src/main/resources/ioSerializedObject.txt
|
Binary file not shown.
|
@ -34,7 +34,9 @@ class DataAndObjectsUnitTest {
|
||||||
@Test
|
@Test
|
||||||
void whenUsingWithObjectOutputStream_thenObjectIsSerializedToFile() {
|
void whenUsingWithObjectOutputStream_thenObjectIsSerializedToFile() {
|
||||||
Task task = new Task(description:'Take out the trash', startDate:new Date(), status:0)
|
Task task = new Task(description:'Take out the trash', startDate:new Date(), status:0)
|
||||||
new File('src/main/resources/ioSerializedObject.txt').withObjectOutputStream { out ->
|
def serializedDataFile = new File('src/main/resources/ioSerializedObject.txt')
|
||||||
|
serializedDataFile.createNewFile()
|
||||||
|
serializedDataFile.withObjectOutputStream { out ->
|
||||||
out.writeObject(task)
|
out.writeObject(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
## Relevant articles:
|
||||||
|
|
||||||
|
- [Multi-Module Maven Application with Java Modules](https://www.baeldung.com/maven-multi-module-project-java-jpms)
|
|
@ -12,6 +12,7 @@
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>parent-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<build>
|
<build>
|
|
@ -12,6 +12,7 @@
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>parent-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
0
core-java-11/src/main/java/com/baeldung/add → core-java-modules/core-java-11/src/main/java/com/baeldung/add
Executable file → Normal file
0
core-java-11/src/main/java/com/baeldung/add → core-java-modules/core-java-11/src/main/java/com/baeldung/add
Executable file → Normal file
|
@ -14,6 +14,7 @@
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>parent-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
|
@ -14,16 +14,23 @@
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-java</artifactId>
|
<artifactId>parent-java</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../parent-java</relativePath>
|
<relativePath>../../parent-java</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<icu.version>64.2</icu.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ibm.icu</groupId>
|
||||||
|
<artifactId>icu4j</artifactId>
|
||||||
|
<version>${icu.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.baeldung.jarArguments;
|
||||||
|
|
||||||
|
public class JarExample {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Hello Baeldung Reader in JarExample!");
|
||||||
|
|
||||||
|
if(args == null) {
|
||||||
|
System.out.println("You have not provided any arguments!");
|
||||||
|
}else {
|
||||||
|
System.out.println("There are "+args.length+" argument(s)!");
|
||||||
|
for(int i=0; i<args.length; i++) {
|
||||||
|
System.out.println("Argument("+(i+1)+"):" + args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.baeldung.localization;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class App {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs all available formatter
|
||||||
|
* @throws ParseException
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
List<Locale> locales = Arrays.asList(new Locale[] { Locale.UK, Locale.ITALY, Locale.FRANCE, Locale.forLanguageTag("pl-PL") });
|
||||||
|
Localization.run(locales);
|
||||||
|
JavaSEFormat.run(locales);
|
||||||
|
ICUFormat.run(locales);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.baeldung.localization;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import com.ibm.icu.text.MessageFormat;
|
||||||
|
|
||||||
|
public class ICUFormat {
|
||||||
|
|
||||||
|
public static String getLabel(Locale locale, Object[] data) {
|
||||||
|
ResourceBundle bundle = ResourceBundle.getBundle("formats", locale);
|
||||||
|
String format = bundle.getString("label-icu");
|
||||||
|
MessageFormat formatter = new MessageFormat(format, locale);
|
||||||
|
return formatter.format(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void run(List<Locale> locales) {
|
||||||
|
System.out.println("ICU formatter");
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Alice", "female", 0 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Alice", "female", 1 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Alice", "female", 2 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Alice", "female", 3 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Bob", "male", 0 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Bob", "male", 1 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Bob", "male", 2 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { "Bob", "male", 3 })));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.baeldung.localization;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class JavaSEFormat {
|
||||||
|
|
||||||
|
public static String getLabel(Locale locale, Object[] data) {
|
||||||
|
ResourceBundle bundle = ResourceBundle.getBundle("formats", locale);
|
||||||
|
final String pattern = bundle.getString("label");
|
||||||
|
final MessageFormat formatter = new MessageFormat(pattern, locale);
|
||||||
|
return formatter.format(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void run(List<Locale> locales) {
|
||||||
|
System.out.println("Java formatter");
|
||||||
|
final Date date = new Date(System.currentTimeMillis());
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { date, "Alice", 0 })));
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale, new Object[] { date, "Alice", 2 })));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.baeldung.localization;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class Localization {
|
||||||
|
|
||||||
|
public static String getLabel(Locale locale) {
|
||||||
|
final ResourceBundle bundle = ResourceBundle.getBundle("messages", locale);
|
||||||
|
return bundle.getString("label");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void run(List<Locale> locales) {
|
||||||
|
locales.forEach(locale -> System.out.println(getLabel(locale)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Main-Class: com.baeldung.jarArguments.JarExample
|
|
@ -0,0 +1,2 @@
|
||||||
|
label=On {0, date, short} {1} has sent you {2, choice, 0#no messages|1#a message|2#two messages|2<{2,number,integer} messages}.
|
||||||
|
label-icu={0} has sent you {2, plural, =0 {no messages} =1 {a message} other {{2, number, integer} messages}}.
|
|
@ -0,0 +1,2 @@
|
||||||
|
label={0, date, short}, {1}{2, choice, 0# ne|0<} vous a envoyé {2, choice, 0#aucun message|1#un message|2#deux messages|2<{2,number,integer} messages}.
|
||||||
|
label-icu={0} {2, plural, =0 {ne } other {}}vous a envoyé {2, plural, =0 {aucun message} =1 {un message} other {{2, number, integer} messages}}.
|
|
@ -0,0 +1,2 @@
|
||||||
|
label={0, date, short} {1} ti ha inviato {2, choice, 0#nessun messagio|1#un messaggio|2#due messaggi|2<{2, number, integer} messaggi}.
|
||||||
|
label-icu={0} {2, plural, =0 {non } other {}}ti ha inviato {2, plural, =0 {nessun messaggio} =1 {un messaggio} other {{2, number, integer} messaggi}}.
|
|
@ -0,0 +1,2 @@
|
||||||
|
label=W {0, date, short} {1}{2, choice, 0# nie|0<} wys\u0142a\u0142a ci {2, choice, 0#\u017Cadnych wiadomo\u015Bci|1#wiadomo\u015B\u0107|2#dwie wiadomo\u015Bci|2<{2, number, integer} wiadomo\u015Bci}.
|
||||||
|
label-icu={0} {2, plural, =0 {nie } other {}}{1, select, male {wys\u0142a\u0142} female {wys\u0142a\u0142a} other {wys\u0142a\u0142o}} ci {2, plural, =0 {\u017Cadnej wiadomo\u015Bci} =1 {wiadomo\u015B\u0107} other {{2, number, integer} wiadomo\u015Bci}}.
|
|
@ -0,0 +1 @@
|
||||||
|
label=Alice has sent you a message.
|
|
@ -0,0 +1 @@
|
||||||
|
label=Alice vous a envoyé un message.
|
|
@ -0,0 +1 @@
|
||||||
|
label=Alice ti ha inviato un messaggio.
|
|
@ -0,0 +1 @@
|
||||||
|
label=Alice wys\u0142a\u0142a ci wiadomo\u015B\u0107.
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.baeldung.localization;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.localization.ICUFormat;
|
||||||
|
|
||||||
|
public class ICUFormatUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInUK_whenAliceSendsNothing_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice has sent you no messages.", ICUFormat.getLabel(Locale.UK, new Object[] { "Alice", "female", 0 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInUK_whenAliceSendsOneMessage_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice has sent you a message.", ICUFormat.getLabel(Locale.UK, new Object[] { "Alice", "female", 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInUK_whenBobSendsSixMessages_thenCorrectMessage() {
|
||||||
|
assertEquals("Bob has sent you 6 messages.", ICUFormat.getLabel(Locale.UK, new Object[] { "Bob", "male", 6 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInItaly_whenAliceSendsNothing_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice non ti ha inviato nessun messaggio.", ICUFormat.getLabel(Locale.ITALY, new Object[] { "Alice", "female", 0 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInItaly_whenAliceSendsOneMessage_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice ti ha inviato un messaggio.", ICUFormat.getLabel(Locale.ITALY, new Object[] { "Alice", "female", 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInItaly_whenBobSendsSixMessages_thenCorrectMessage() {
|
||||||
|
assertEquals("Bob ti ha inviato 6 messaggi.", ICUFormat.getLabel(Locale.ITALY, new Object[] { "Bob", "male", 6 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInFrance_whenAliceSendsNothing_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice ne vous a envoyé aucun message.", ICUFormat.getLabel(Locale.FRANCE, new Object[] { "Alice", "female", 0 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInFrance_whenAliceSendsOneMessage_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice vous a envoyé un message.", ICUFormat.getLabel(Locale.FRANCE, new Object[] { "Alice", "female", 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInFrance_whenBobSendsSixMessages_thenCorrectMessage() {
|
||||||
|
assertEquals("Bob vous a envoyé 6 messages.", ICUFormat.getLabel(Locale.FRANCE, new Object[] { "Bob", "male", 6 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInPoland_whenAliceSendsNothing_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice nie wysłała ci żadnej wiadomości.", ICUFormat.getLabel(Locale.forLanguageTag("pl-PL"), new Object[] { "Alice", "female", 0 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInPoland_whenAliceSendsOneMessage_thenCorrectMessage() {
|
||||||
|
assertEquals("Alice wysłała ci wiadomość.", ICUFormat.getLabel(Locale.forLanguageTag("pl-PL"), new Object[] { "Alice", "female", 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInPoland_whenBobSendsSixMessages_thenCorrectMessage() {
|
||||||
|
assertEquals("Bob wysłał ci 6 wiadomości.", ICUFormat.getLabel(Locale.forLanguageTag("pl-PL"), new Object[] { "Bob", "male", 6 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,7 +11,7 @@
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-java</artifactId>
|
<artifactId>parent-java</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../parent-java</relativePath>
|
<relativePath>../../parent-java</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue