From a3d16d688f94c7c577fc65374b2541fa2b7e5bb5 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Sun, 12 Dec 2021 13:03:54 +0100 Subject: [PATCH] [#1404, #1405] Add JSONB integration and make sure that GraphQL mutations work --- CHANGELOG.md | 6 +- .../checkstyle-suppressions.xml | 2 + dist/bom/pom.xml | 44 +- dist/full/pom.xml | 16 + dist/full/src/main/distribution/assembly.xml | 9 +- .../entity-view/manual/en_US/graphql.adoc | 85 ++- .../entity-view/manual/en_US/jaxrs.adoc | 32 +- .../manual/en_US/serialization_support.adoc | 81 ++- examples/deltaspike-data-rest/pom.xml | 4 + examples/microprofile-graphql/pom.xml | 12 +- .../microprofile/graphql/GraphQLProducer.java | 29 +- .../graphql/resource/CatResource.java | 35 +- .../graphql/view/CatCreateView.java | 13 - examples/quarkus/base/pom.xml | 2 +- .../spring/data/dgs/GraphQLProvider.java | 1 - examples/spring-data-spqr/pom.xml | 35 +- .../spring/data/spqr/Application.java | 2 +- .../config/BlazePersistenceConfiguration.java | 2 +- .../data/spqr/config/SpqrConfiguration.java | 50 +- .../examples/spring/data/spqr/model/Cat.java | 2 +- .../spring/data/spqr/model/Person.java | 2 +- .../spqr/repository/CatJpaRepository.java | 2 +- .../spqr/repository/CatViewRepository.java | 2 +- .../data/spqr/resource/CatGraphQLApi.java | 3 +- .../spring/data/spqr/view/CatCreateView.java | 2 +- .../data/spqr/view/CatSimpleCreateView.java | 2 +- .../spring/data/spqr/view/CatSimpleView.java | 2 +- .../spring/data/spqr/view/CatUpdateView.java | 2 +- .../data/spqr/view/CatWithOwnerView.java | 2 +- .../spring/data/spqr/view/PersonIdView.java | 2 +- .../data/spqr/view/PersonSimpleView.java | 2 +- integration/deltaspike-data/rest/impl/pom.xml | 4 + integration/graphql-spqr-jakarta/pom.xml | 171 ++++++ integration/graphql-spqr/pom.xml | 215 +++++++ ...BlazePersistenceSpqrAutoConfiguration.java | 99 ++++ .../main/resources/META-INF/spring.factories | 1 + .../graphql/spqr/AbstractSampleTest.java | 69 +++ .../integration/graphql/spqr/Application.java | 66 +++ .../integration/graphql/spqr/SampleTest.java | 122 ++++ .../config/BlazePersistenceConfiguration.java | 64 +++ .../spqr/config/SpqrConfiguration.java | 72 +++ .../integration/graphql/spqr/model/Cat.java | 118 ++++ .../graphql/spqr/model/Person.java | 70 +++ .../spqr/repository/CatJpaRepository.java | 28 + .../spqr/repository/CatViewRepository.java | 56 ++ .../graphql/spqr/resource/CatGraphQLApi.java | 79 +++ .../graphql/spqr/view/CatCreateView.java | 35 ++ .../spqr/view/CatSimpleCreateView.java | 34 ++ .../graphql/spqr/view/CatSimpleView.java | 34 ++ .../graphql/spqr/view/CatUpdateView.java | 36 ++ .../graphql/spqr/view/CatWithOwnerView.java | 31 + .../graphql/spqr/view/PersonIdView.java | 35 ++ .../graphql/spqr/view/PersonSimpleView.java | 31 + .../test/resources/META-INF/test-config.xml | 23 + .../src/test/resources/application.properties | 8 + .../src/test/resources/logback-test.xml | 27 + .../GraphQLEntityViewSupportFactory.java | 45 +- .../jackson/EntityViewAwareObjectMapper.java | 2 - .../pom.xml | 10 +- integration/jaxrs-jackson/pom.xml | 253 ++++++++ .../jackson}/EntityViewMessageBodyReader.java | 3 +- ...tyViewIdAwareParameterServiceProvider.java | 73 +++ .../src/main/resources/META-INF/beans.xml | 0 ...odel.internal.spi.ParameterServiceProvider | 1 + .../jackson}/testsuite/AbstractJaxrsTest.java | 10 +- .../testsuite/DocumentResourceTest.java | 10 +- .../FromStringParamConverterProviderTest.java | 100 ++++ .../jackson/testsuite/PersonResourceTest.java | 91 +++ .../CriteriaBuilderFactoryProducer.java | 2 +- .../config/EntityManagerFactoryHolder.java | 2 +- .../testsuite/config/EntityManagerHolder.java | 2 +- .../config/EntityViewManagerProducer.java | 2 +- .../jackson}/testsuite/entity/Document.java | 2 +- .../jackson}/testsuite/entity/Person.java | 2 +- .../testsuite/resource/Application.java | 4 +- .../testsuite/resource/DocumentResource.java | 8 +- .../testsuite/resource/PersonResource.java | 10 +- .../testsuite/view/DocumentUpdateView.java | 4 +- .../jackson}/testsuite/view/DocumentView.java | 4 +- .../testsuite/view/PersonCreateView.java | 4 +- .../testsuite/view/PersonUpdateView.java | 4 +- .../jackson}/testsuite/view/PersonView.java | 4 +- .../src/test/resources/META-INF/beans.xml | 0 .../test/resources/META-INF/persistence.xml | 31 + .../src/test/resources/logging.properties | 0 integration/jaxrs-jsonb-jakarta/pom.xml | 171 ++++++ integration/jaxrs-jsonb/pom.xml | 258 +++++++++ .../jsonb/EntityViewMessageBodyReader.java | 543 ++++++++++++++++++ ...tyViewIdAwareParameterServiceProvider.java | 2 +- .../src/main/resources/META-INF/beans.xml | 24 + ...odel.internal.spi.ParameterServiceProvider | 1 + .../jsonb/testsuite/AbstractJaxrsTest.java | 285 +++++++++ .../jsonb/testsuite/DocumentResourceTest.java | 113 ++++ .../FromStringParamConverterProviderTest.java | 4 +- .../jsonb/testsuite/JsonbJsonProvider.java | 81 +++ .../jsonb}/testsuite/PersonResourceTest.java | 10 +- .../CriteriaBuilderFactoryProducer.java | 59 ++ .../config/EntityManagerFactoryHolder.java | 53 ++ .../testsuite/config/EntityManagerHolder.java | 53 ++ .../config/EntityViewManagerProducer.java | 79 +++ .../jsonb/testsuite/entity/Document.java | 88 +++ .../jaxrs/jsonb/testsuite/entity/Person.java | 90 +++ .../jsonb/testsuite/resource/Application.java | 37 ++ .../testsuite/resource/DocumentResource.java | 78 +++ .../testsuite/resource/PersonResource.java | 71 +++ .../testsuite/view/DocumentUpdateView.java | 37 ++ .../jsonb/testsuite/view/DocumentView.java | 44 ++ .../testsuite/view/PersonCreateView.java | 37 ++ .../testsuite/view/PersonUpdateView.java | 39 ++ .../jsonb/testsuite/view/PersonView.java | 37 ++ .../src/test/resources/META-INF/beans.xml | 19 + .../test/resources/META-INF/persistence.xml | 4 +- .../src/test/resources/logging.properties | 43 ++ integration/jaxrs/pom.xml | 203 ------- ...odel.internal.spi.ParameterServiceProvider | 1 - integration/jsonb-jakarta/pom.xml | 167 ++++++ integration/jsonb/pom.xml | 105 ++++ .../jsonb/EntityViewIdValueAccessor.java | 43 ++ .../jsonb/EntityViewJsonbDeserializer.java | 123 ++++ .../EntityViewPropertyVisibilityStrategy.java | 60 ++ .../EntityViewReferenceDeserializer.java | 220 +++++++ .../jsonstructure/JsonArrayIterator.java | 82 +++ .../jsonstructure/JsonObjectIterator.java | 142 +++++ .../jsonstructure/JsonStructureIterator.java | 85 +++ .../JsonStructureToParserAdapter.java | 167 ++++++ .../JsonValueToParserAdapter.java | 130 +++++ .../EntityViewJsonbDeserializerTest.java | 426 ++++++++++++++ .../integration/jsonb/SomeEntity.java | 45 ++ .../jsonb/StringListConverter.java | 52 ++ .../test/resources/META-INF/persistence.xml | 24 + integration/pom.xml | 9 +- integration/quarkus/deployment/pom.xml | 2 +- parent/pom.xml | 37 +- pom.xml | 1 + website/src/main/jbake/content/downloads.adoc | 19 + 135 files changed, 6776 insertions(+), 423 deletions(-) create mode 100644 integration/graphql-spqr-jakarta/pom.xml create mode 100644 integration/graphql-spqr/pom.xml create mode 100644 integration/graphql-spqr/src/main/java/com/blazebit/persistence/integration/graphql/spqr/BlazePersistenceSpqrAutoConfiguration.java create mode 100644 integration/graphql-spqr/src/main/resources/META-INF/spring.factories create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/AbstractSampleTest.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/Application.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/SampleTest.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/BlazePersistenceConfiguration.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/SpqrConfiguration.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Cat.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Person.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatJpaRepository.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatViewRepository.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/resource/CatGraphQLApi.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatCreateView.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleCreateView.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleView.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatUpdateView.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatWithOwnerView.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonIdView.java create mode 100644 integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonSimpleView.java create mode 100644 integration/graphql-spqr/src/test/resources/META-INF/test-config.xml create mode 100644 integration/graphql-spqr/src/test/resources/application.properties create mode 100644 integration/graphql-spqr/src/test/resources/logback-test.xml rename integration/{jaxrs-jakarta => jaxrs-jackson-jakarta}/pom.xml (96%) create mode 100644 integration/jaxrs-jackson/pom.xml rename integration/{jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson}/EntityViewMessageBodyReader.java (99%) create mode 100644 integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/jersey/EntityViewIdAwareParameterServiceProvider.java rename integration/{jaxrs => jaxrs-jackson}/src/main/resources/META-INF/beans.xml (100%) create mode 100644 integration/jaxrs-jackson/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/AbstractJaxrsTest.java (94%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/DocumentResourceTest.java (89%) create mode 100644 integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/FromStringParamConverterProviderTest.java create mode 100644 integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/PersonResourceTest.java rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/config/CriteriaBuilderFactoryProducer.java (96%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/config/EntityManagerFactoryHolder.java (95%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/config/EntityManagerHolder.java (94%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/config/EntityViewManagerProducer.java (97%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/entity/Document.java (96%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/entity/Person.java (96%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/resource/Application.java (92%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/resource/DocumentResource.java (87%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/resource/PersonResource.java (82%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/view/DocumentUpdateView.java (86%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/view/DocumentView.java (87%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/view/PersonCreateView.java (86%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/view/PersonUpdateView.java (86%) rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson}/testsuite/view/PersonView.java (84%) rename integration/{jaxrs => jaxrs-jackson}/src/test/resources/META-INF/beans.xml (100%) create mode 100644 integration/jaxrs-jackson/src/test/resources/META-INF/persistence.xml rename integration/{jaxrs => jaxrs-jackson}/src/test/resources/logging.properties (100%) create mode 100644 integration/jaxrs-jsonb-jakarta/pom.xml create mode 100644 integration/jaxrs-jsonb/pom.xml create mode 100644 integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java rename integration/{jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb}/jersey/EntityViewIdAwareParameterServiceProvider.java (98%) create mode 100644 integration/jaxrs-jsonb/src/main/resources/META-INF/beans.xml create mode 100644 integration/jaxrs-jsonb/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/AbstractJaxrsTest.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/DocumentResourceTest.java rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb}/testsuite/FromStringParamConverterProviderTest.java (95%) create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/JsonbJsonProvider.java rename integration/{jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs => jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb}/testsuite/PersonResourceTest.java (87%) create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/CriteriaBuilderFactoryProducer.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerFactoryHolder.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerHolder.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityViewManagerProducer.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Document.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Person.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/Application.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/DocumentResource.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/PersonResource.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentUpdateView.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentView.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonCreateView.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonUpdateView.java create mode 100644 integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonView.java create mode 100644 integration/jaxrs-jsonb/src/test/resources/META-INF/beans.xml rename integration/{jaxrs => jaxrs-jsonb}/src/test/resources/META-INF/persistence.xml (88%) create mode 100644 integration/jaxrs-jsonb/src/test/resources/logging.properties delete mode 100644 integration/jaxrs/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider create mode 100644 integration/jsonb-jakarta/pom.xml create mode 100644 integration/jsonb/pom.xml create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewIdValueAccessor.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializer.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewPropertyVisibilityStrategy.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewReferenceDeserializer.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonArrayIterator.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonObjectIterator.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureIterator.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureToParserAdapter.java create mode 100644 integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonValueToParserAdapter.java create mode 100644 integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializerTest.java create mode 100644 integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/SomeEntity.java create mode 100644 integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/StringListConverter.java create mode 100644 integration/jsonb/src/test/resources/META-INF/persistence.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 21746ea3cb..756b1fc988 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,9 @@ Not yet released * Add `PERCENTILE_CONT`, `PERCENTILE_DISC` and `MODE` ordered set-aggregate functions * Add JPA Criteria support for aggregate `FILTER` clause, window functions and ordered set aggregate functions * Fire `EntityViewConfiguration` as event in Quarkus extension boot like in the CDI integration to allow customization -* Add Spring SPQR example application showcasing how GraphQL mutations can be used +* Add JSONB integration for deserializing entity views +* Add Spring SPQR integration and example application +* Add examples for SPQR, DGS and MicroProfile GraphQL showcasing how GraphQL mutations can be used ### Bug fixes @@ -24,10 +26,12 @@ Not yet released * Add `getById` implementation as needed by Spring Data 2.5 to the integration * Ensure stream/iteration processing of results works with entity views containing no collections * Fix support for flushing flat view collections with read-only declared and updatable subview types +* Ensure pending inserts for tables targeted by foreign keys by custom insert/update DML are flushed ### Backwards-incompatible changes * Properly implement the `getOne` semantics to return a reference instead of querying an instance +* Split JAX-RS integration into JAX-RS for Jackson and JAX-RS for JSONB integrations ## 1.6.3 diff --git a/checkstyle-rules/src/main/resources/blaze-persistence/checkstyle-suppressions.xml b/checkstyle-rules/src/main/resources/blaze-persistence/checkstyle-suppressions.xml index 2149975454..7361ba47a8 100644 --- a/checkstyle-rules/src/main/resources/blaze-persistence/checkstyle-suppressions.xml +++ b/checkstyle-rules/src/main/resources/blaze-persistence/checkstyle-suppressions.xml @@ -44,6 +44,8 @@ + + diff --git a/dist/bom/pom.xml b/dist/bom/pom.xml index 1263ed55a2..5388328e2e 100644 --- a/dist/bom/pom.xml +++ b/dist/bom/pom.xml @@ -364,11 +364,29 @@ com.blazebit blaze-persistence-integration-jaxrs ${project.version} + compile + + + com.blazebit + blaze-persistence-integration-jaxrs-jackson + ${project.version} + runtime + + + com.blazebit + blaze-persistence-integration-jaxrs-jsonb + ${project.version} runtime com.blazebit - blaze-persistence-integration-jaxrs-jakarta + blaze-persistence-integration-jaxrs-jackson-jakarta + ${project.version} + runtime + + + com.blazebit + blaze-persistence-integration-jaxrs-jsonb-jakarta ${project.version} runtime @@ -384,6 +402,18 @@ ${project.version} compile + + com.blazebit + blaze-persistence-integration-jsonb + ${project.version} + compile + + + com.blazebit + blaze-persistence-integration-jsonb-jakarta + ${project.version} + compile + com.blazebit blaze-persistence-integration-graphql @@ -396,6 +426,18 @@ ${project.version} compile + + com.blazebit + blaze-persistence-integration-graphql-spqr + ${project.version} + compile + + + com.blazebit + blaze-persistence-integration-graphql-spqr-jakarta + ${project.version} + compile + com.blazebit blaze-persistence-integration-quarkus diff --git a/dist/full/pom.xml b/dist/full/pom.xml index 2d1c5cb00c..5421d20852 100644 --- a/dist/full/pom.xml +++ b/dist/full/pom.xml @@ -199,6 +199,10 @@ com.blazebit blaze-persistence-integration-graphql + + com.blazebit + blaze-persistence-integration-graphql-spqr + com.blazebit blaze-persistence-integration-spring-hateoas-webmvc @@ -207,10 +211,22 @@ com.blazebit blaze-persistence-integration-jackson + + com.blazebit + blaze-persistence-integration-jsonb + com.blazebit blaze-persistence-integration-jaxrs + + com.blazebit + blaze-persistence-integration-jaxrs-jackson + + + com.blazebit + blaze-persistence-integration-jaxrs-jsonb + com.blazebit blaze-persistence-integration-quarkus diff --git a/dist/full/src/main/distribution/assembly.xml b/dist/full/src/main/distribution/assembly.xml index db814a9115..f0cf4fe288 100644 --- a/dist/full/src/main/distribution/assembly.xml +++ b/dist/full/src/main/distribution/assembly.xml @@ -116,12 +116,9 @@ com.blazebit:blaze-persistence-integration-entity-view-* com.blazebit:blaze-persistence-integration-spring-* com.blazebit:blaze-persistence-integration-deltaspike-data-* - com.blazebit:blaze-persistence-integration-jaxrs - com.blazebit:blaze-persistence-integration-jaxrs-jakarta - com.blazebit:blaze-persistence-integration-jackson - com.blazebit:blaze-persistence-integration-jackson-jakarta - com.blazebit:blaze-persistence-integration-graphql - com.blazebit:blaze-persistence-integration-graphql-jakarta + com.blazebit:blaze-persistence-integration-jaxrs* + com.blazebit:blaze-persistence-integration-jackson* + com.blazebit:blaze-persistence-integration-graphql* ${artifact.artifactId}-${project.version}.${artifact.extension} lib/integration/entity-view diff --git a/documentation/src/main/asciidoc/entity-view/manual/en_US/graphql.adoc b/documentation/src/main/asciidoc/entity-view/manual/en_US/graphql.adoc index bd553d3f60..d2f81cb1ab 100644 --- a/documentation/src/main/asciidoc/entity-view/manual/en_US/graphql.adoc +++ b/documentation/src/main/asciidoc/entity-view/manual/en_US/graphql.adoc @@ -28,7 +28,7 @@ or if you are using Jakarta JPA ---- com.blazebit - blaze-persistence-integration-graphql + blaze-persistence-integration-graphql-jakarta ${blaze-persistence.version} compile @@ -238,6 +238,86 @@ public class CatFetcher { For a full example see the following https://github.com/Blazebit/blaze-persistence/blob/master/examples/spring-data-graphql/[example project]. +==== SPQR setup + +To use the SPQR GraphQL integration you need the following Maven dependencies: + +[source,xml] +---- + + com.blazebit + blaze-persistence-integration-graphql-spqr + ${blaze-persistence.version} + compile + +---- + +or if you are using Jakarta JPA + +[source,xml] +---- + + com.blazebit + blaze-persistence-integration-graphql-spqr-jakarta + ${blaze-persistence.version} + compile + +---- + +The SPQR configuration is very simple and since the framework is fully declarative, you don't need a dedicated GraphQL schema definition. + +[source,java] +---- +@Configuration +public class GraphQLProvider { + + @Autowired + EntityViewManager evm; + @Autowired + GraphQLSchema graphQLSchema; + + private GraphQLEntityViewSupport graphQLEntityViewSupport; + + @PostConstruct + public void init() { + GraphQLEntityViewSupportFactory graphQLEntityViewSupportFactory = new GraphQLEntityViewSupportFactory(false, false); + graphQLEntityViewSupportFactory.setImplementRelayNode(false); + graphQLEntityViewSupportFactory.setDefineRelayNodeIfNotExist(false); + this.graphQLEntityViewSupport = graphQLEntityViewSupportFactory.create(graphQLSchema, evm); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) + @Lazy(false) + public GraphQLEntityViewSupport graphQLEntityViewSupport() { + return graphQLEntityViewSupport; + } + +} +---- + +Next, one needs to define a `DataFetcher` for the defined query `catById` like so + +[source,java] +---- +@Component +@GraphQLApi +public class CatFetcher { + + @Autowired + CatViewRepository repository; + @Autowired + GraphQLEntityViewSupport graphQLEntityViewSupport; + + @GraphQLQuery + public CatWithOwnerView catById(@GraphQLArgument(name = "id") Long id, @GraphQLEnvironment ResolutionEnvironment env) { + return repository.findById(graphQLEntityViewSupport.createSetting(env.dataFetchingEnvironment), id); + } +} +---- + +For a full example see the following https://github.com/Blazebit/blaze-persistence/blob/master/examples/spring-data-graphql/[example project]. + ==== MicroProfile GraphQL - SmallRye MicroProfile GraphQL (version 1.1 at the time of writing) has a completely different approach, as it is completely annotation based. @@ -575,4 +655,5 @@ For a full example see one of the following example projects: * https://github.com/Blazebit/blaze-persistence/blob/master/examples/spring-data-graphql/[Plain graphql-java] * https://github.com/Blazebit/blaze-persistence/blob/master/examples/spring-data-dgs/[Netflix DGS] -* https://github.com/Blazebit/blaze-persistence/blob/master/examples/microprofile-graphql/[MicroProfile GraphQL] \ No newline at end of file +* https://github.com/Blazebit/blaze-persistence/blob/master/examples/microprofile-graphql/[MicroProfile GraphQL] +* https://github.com/Blazebit/blaze-persistence/blob/master/examples/spring-data-spqr/[SPQR] \ No newline at end of file diff --git a/documentation/src/main/asciidoc/entity-view/manual/en_US/jaxrs.adoc b/documentation/src/main/asciidoc/entity-view/manual/en_US/jaxrs.adoc index e247831a4b..28349b0219 100644 --- a/documentation/src/main/asciidoc/entity-view/manual/en_US/jaxrs.adoc +++ b/documentation/src/main/asciidoc/entity-view/manual/en_US/jaxrs.adoc @@ -1,9 +1,11 @@ [[jaxrs-integration]] == JAX-RS integration -The JAX-RS integration provides `MessageBodyReader` and `ParamConverter` implementations to integrate the following serialization integrations with JAX-RS +The JAX-RS integration module serves as the API which contains the `@EntityViewId` annotation. +The `MessageBodyReader` and `ParamConverter` implementations to integrate serialization frameworks with JAX-RS are available for * <> +* <> * _At some point there will also be support for JAXB_ The integration is discovered automatically through the `javax.ws.rs.ext.Providers` ServiceLoader contract. Simply putting the artifact on the classpath is enough for the integration. @@ -17,7 +19,7 @@ To use the Jackson integration directly you need the following Maven dependencie ---- com.blazebit - blaze-persistence-integration-jaxrs + blaze-persistence-integration-jaxrs-jackson ${blaze-persistence.version} runtime @@ -29,7 +31,31 @@ or if you are using Jakarta JPA ---- com.blazebit - blaze-persistence-integration-jaxrs-jakarta + blaze-persistence-integration-jaxrs-jackson-jakarta + ${blaze-persistence.version} + runtime + +---- + +To use the JSONB integration directly you need the following Maven dependencies: + +[source,xml] +---- + + com.blazebit + blaze-persistence-integration-jaxrs-jsonb + ${blaze-persistence.version} + runtime + +---- + +or if you are using Jakarta JPA + +[source,xml] +---- + + com.blazebit + blaze-persistence-integration-jaxrs-jsonb-jakarta ${blaze-persistence.version} runtime diff --git a/documentation/src/main/asciidoc/entity-view/manual/en_US/serialization_support.adoc b/documentation/src/main/asciidoc/entity-view/manual/en_US/serialization_support.adoc index 0a84dadca1..538bcbeb00 100644 --- a/documentation/src/main/asciidoc/entity-view/manual/en_US/serialization_support.adoc +++ b/documentation/src/main/asciidoc/entity-view/manual/en_US/serialization_support.adoc @@ -6,6 +6,29 @@ and entity views naturally define their state through getters. The deserialization support is a different thing though for which special integrations are required. +By default, the deserialization will invoke `EntityViewManager.getReference(viewType, id)` to construct objects. +If the view type that should be deserialized is creatable i.e. annotated with `@CreatableEntityView` and no value for the `@IdMapping` attribute is provided in the payload, +it will be created via `EntityViewManager.create()`. +The rest of the payload values are then deserialized onto the constructed object via the setters the entity view types provide. + +Read-only types only support deserialization when a value for the `@IdMapping` attribute is given and obviously need setters for the deserialization to work properly. +Adding setters makes the models mutable which might not be desirable, so consider this when wanting to deserialize payload to entity views. +A possible solution is to create an entity view subtype that has the sole purpose of providing setters so that it can be deserialized. + +Since entity view objects are created via `EntityViewManager.getReference()` or `EntityViewManager.create()`, the objects can then be directly saved via `EntityViewManager.save()`. +For updatable entity views it is important to understand that `EntityViewManager.save()` will only flush non-null attributes i.e. do a partial flush. +This is due to dirty tracking thinking that the initial state is all null attributes. Through deserialization, some attributes are set to values which are then considered dirty. +That behavior is exactly what you would expect from an HTTP endpoint using the `PATCH` method. + +If you need to flush the full state regardless of the dirty tracking information, you can use `EntityViewManager.saveFull()`, +but be aware that orphan removals will currently not work when using the `QUERY` flush strategy because the initial state is unknown. +If you need orphan removal in such a case, you are advised to switch to the `ENTITY` flush strategy for now. +You could also make use of the `EntityViewManager.saveTo()` and `EntityViewManager.saveFullTo()` variants to flush data to an entity that was loaded via e.g. `EntityManager.find()`. + +An alternative to this would be to deserialize the state onto an existing updatable entity view that was loaded via e.g. `EntityViewManager.find()`. +With the initial state being known due to loading from the database, orphan removal will work correctly, +but be aware that providing `null` values for attributes in the JSON payload will obviously cause `null` to be set on the entity view attributes. + [[jackson-integration]] === Jackson integration @@ -52,25 +75,47 @@ EntityViewAwareObjectMapper mapper = new EntityViewAwareObjectMapper(evm, existi The `EntityViewAwareObjectMapper` class provides utility methods for integrating with JAX-RS, Spring WebMvc and Spring WebFlux, but you can use your `ObjectMapper` directly as before as the module and visibility checker is registered in the existing mapper. -By default, the deserialization will invoke `EntityViewManager.getReference(viewType, id)` to construct objects. -If the view type that should be deserialized is creatable i.e. annotated with `@CreatableEntityView` and no value for the `@IdMapping` attribute is provided in the JSON, -it will be created via `EntityViewManager.create()`. -The rest of the JSON values are then deserialized onto the constructed object via the setters the entity view types provide. +[[jsonb-integration]] +=== JSONB integration -Read-only types only support deserialization when a value for the `@IdMapping` attribute is given and obviously need setters for the deserialization to work properly. -Adding setters makes the models mutable which might not be desirable, so consider this when wanting to deserialize JSON to entity views. -A possible solution is to create an entity view subtype that has the sole purpose of providing setters so that it can be deserialized. +[[jsonb-setup]] +==== Setup -Since entity view objects are created via `EntityViewManager.getReference()` or `EntityViewManager.create()`, the objects can then be directly saved via `EntityViewManager.save()`. -For updatable entity views it is important to understand that `EntityViewManager.save()` will only flush non-null attributes i.e. do a partial flush. -This is due to dirty tracking thinking that the initial state is all null attributes. Through deserialization, some attributes are set to values which are then considered dirty. -That behavior is exactly what you would expect from a HTTP endpoint using the `PATCH` method. +To use the JSONB integration directly you need the following Maven dependencies: -If you need to flush the full state regardless of the dirty tracking information, you can use `EntityViewManager.saveFull()`, -but be aware that orphan removals will currently not work when using the `QUERY` flush strategy because the initial state is unknown. -If you need orphan removal in such a case, you are advised to switch to the `ENTITY` flush strategy for now. -You could also make use of the `EntityViewManager.saveTo()` and `EntityViewManager.saveFullTo()` variants to flush data to an entity that was loaded via e.g. `EntityManager.find()`. +[source,xml] +---- + + com.blazebit + blaze-persistence-integration-jsonb + ${blaze-persistence.version} + compile + +---- -An alternative to this would be to deserialize the state onto an existing updatable entity view that was loaded via e.g. `EntityViewManager.find()`. -With the initial state being known due to loading from the database, orphan removal will work correctly, -but be aware that providing `null` values for attributes in the JSON payload will obviously cause `null` to be set on the entity view attributes. \ No newline at end of file +or if you are using Jakarta JPA + +[source,xml] +---- + + com.blazebit + blaze-persistence-integration-jsonb-jakarta + ${blaze-persistence.version} + compile + +---- + +If you are using JAX-RS, Spring WebMvc or Spring WebFlux, consider using the already existing integrations instead to avoid unnecessary work. + +==== Usage + +The integration happens by adding custom `JsonDeserializer` instances per entity view and a `PropertyVisibilityStrategy` to a `JsonbConfig`. + +[source,java] +---- +JsonbConfig jsonbConfig = new JsonbConfig(); +EntityViewJsonbDeserializer.integrate(jsonbConfig, evm, idValueAccessor); +Jsonb jsonb = JsonbBuilder.create(jsonbConfig); +---- + +The resulting `Jsonb` instance will make use of the special deserializers for entity views and you can simply use it as usual e.g. `jsonb.fromString("...", MyEntityView.class)`. \ No newline at end of file diff --git a/examples/deltaspike-data-rest/pom.xml b/examples/deltaspike-data-rest/pom.xml index ea4f4f1ebc..22467778f6 100644 --- a/examples/deltaspike-data-rest/pom.xml +++ b/examples/deltaspike-data-rest/pom.xml @@ -137,6 +137,10 @@ ${project.groupId} blaze-persistence-integration-jaxrs + + ${project.groupId} + blaze-persistence-integration-jaxrs-jackson + diff --git a/examples/microprofile-graphql/pom.xml b/examples/microprofile-graphql/pom.xml index 18444a9cd4..fda18e05f4 100644 --- a/examples/microprofile-graphql/pom.xml +++ b/examples/microprofile-graphql/pom.xml @@ -125,6 +125,12 @@ smallrye-graphql-ui-graphiql 1.3.5 + + org.eclipse + yasson + 1.0.9 + provided + ${project.groupId} blaze-persistence-integration-hibernate-base @@ -139,7 +145,11 @@ ${project.groupId} - blaze-persistence-integration-jaxrs + blaze-persistence-integration-jsonb + + + ${project.groupId} + blaze-persistence-integration-jaxrs-jsonb diff --git a/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/GraphQLProducer.java b/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/GraphQLProducer.java index 0d118ca7d3..385841f692 100644 --- a/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/GraphQLProducer.java +++ b/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/GraphQLProducer.java @@ -18,14 +18,22 @@ import com.blazebit.persistence.integration.graphql.GraphQLEntityViewSupport; import com.blazebit.persistence.integration.graphql.GraphQLEntityViewSupportFactory; +import com.blazebit.persistence.integration.jsonb.EntityViewJsonbDeserializer; import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.metamodel.ManagedViewType; import graphql.schema.GraphQLSchema; +import io.smallrye.graphql.json.JsonBCreator; import io.smallrye.graphql.scalar.GraphQLScalarTypes; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Observes; import javax.enterprise.inject.Produces; import javax.inject.Inject; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbConfig; +import java.lang.reflect.Field; +import java.util.Map; /** * @author Christian Beikov @@ -46,7 +54,7 @@ void configure(@Observes GraphQLSchema.Builder schemaBuilder) { // Option 2: Let the integration replace the entity view GraphQL types boolean defineNormalTypes = true; - boolean defineRelayTypes = false; + boolean defineRelayTypes = true; // Configure how to integrate entity views GraphQLEntityViewSupportFactory graphQLEntityViewSupportFactory = new GraphQLEntityViewSupportFactory(defineNormalTypes, defineRelayTypes); @@ -55,6 +63,25 @@ void configure(@Observes GraphQLSchema.Builder schemaBuilder) { graphQLEntityViewSupportFactory.setDefineRelayNodeIfNotExist(true); graphQLEntityViewSupportFactory.setScalarTypeMap(GraphQLScalarTypes.getScalarMap()); this.graphQLEntityViewSupport = graphQLEntityViewSupportFactory.create(schemaBuilder, evm); + // The integration with SmallRye GraphQL requires setting custom Jsonb objects in a registry + // TODO: move this into a separate integration for SmallRye? + Map jsonMap; + try { + Field jsonMapField = JsonBCreator.class.getDeclaredField("jsonMap"); + jsonMapField.setAccessible(true); + jsonMap = (Map) jsonMapField.get(null); + JsonbConfig jsonbConfig = new JsonbConfig() + .withFormatting(true) + .withNullValues(true) + .setProperty("jsonb.fail-on-unknown-properties", true); + EntityViewJsonbDeserializer.integrate(jsonbConfig, evm); + Jsonb jsonb = JsonbBuilder.create(jsonbConfig); + for (ManagedViewType managedView : evm.getMetamodel().getManagedViews()) { + jsonMap.put(managedView.getJavaType().getName(), jsonb); + } + } catch (Exception ex) { + throw new RuntimeException("Couldn't register jsonb objects for deserialization!", ex); + } } @Produces diff --git a/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/resource/CatResource.java b/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/resource/CatResource.java index 0a632be9a9..de101db3d9 100644 --- a/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/resource/CatResource.java +++ b/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/resource/CatResource.java @@ -33,7 +33,6 @@ import graphql.schema.DataFetchingEnvironment; import io.smallrye.graphql.api.Context; import org.eclipse.microprofile.graphql.GraphQLApi; -import org.eclipse.microprofile.graphql.Input; import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Query; @@ -100,18 +99,28 @@ public List getCats() { return evm.applySetting(graphQLEntityViewSupport.createSetting(context.unwrap(DataFetchingEnvironment.class)), cb).getResultList(); } -// @Query -// public GraphQLRelayConnection findAll(@Name("first") Integer first, @Name("last") Integer last, @Name("offset") Integer offset, @Name("before") String before, @Name("after") String after) { -// CriteriaBuilder cb = cbf.create(em, Cat.class); -// EntityViewSetting> setting = graphQLEntityViewSupport.createPaginatedSetting( -// context.unwrap(DataFetchingEnvironment.class) -// ); -// setting.addAttributeSorter("id", Sorters.ascending()); -// if (setting.getMaxResults() == 0) { -// return new GraphQLRelayConnection<>(); -// } -// return new GraphQLRelayConnection<>(evm.applySetting(setting, cb).getResultList()); -// } + @Query + public CatWithOwnerView catById(@Name("id") Long id) { + return evm.find(em, graphQLEntityViewSupport.createSetting(context.unwrap(DataFetchingEnvironment.class)), id); + } + + @Query + public GraphQLRelayConnection findAll( + @Name("first") Integer first, + @Name("last") Integer last, + @Name("offset") Integer offset, + @Name("before") String before, + @Name("after") String after) { + CriteriaBuilder cb = cbf.create(em, Cat.class); + EntityViewSetting> setting = graphQLEntityViewSupport.createPaginatedSetting( + context.unwrap(DataFetchingEnvironment.class) + ); + setting.addAttributeSorter("id", Sorters.ascending()); + if (setting.getMaxResults() == 0) { + return new GraphQLRelayConnection<>(); + } + return new GraphQLRelayConnection<>(evm.applySetting(setting, cb).getResultList()); + } @Mutation @Transactional diff --git a/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/view/CatCreateView.java b/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/view/CatCreateView.java index 2351379716..20884003e0 100644 --- a/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/view/CatCreateView.java +++ b/examples/microprofile-graphql/src/main/java/com/blazebit/persistence/examples/microprofile/graphql/view/CatCreateView.java @@ -20,11 +20,6 @@ import com.blazebit.persistence.view.CreatableEntityView; import com.blazebit.persistence.view.EntityView; -import javax.json.bind.annotation.JsonbTypeDeserializer; -import javax.json.bind.serializer.DeserializationContext; -import javax.json.bind.serializer.JsonbDeserializer; -import javax.json.stream.JsonParser; -import java.lang.reflect.Type; import java.util.Set; /** @@ -33,16 +28,8 @@ */ @CreatableEntityView @EntityView(Cat.class) -@JsonbTypeDeserializer(CatCreateView.MyDeserializer.class) public interface CatCreateView extends CatSimpleCreateView { Set getKittens(); void setKittens(Set kittens); - - class MyDeserializer implements JsonbDeserializer { - @Override - public CatCreateView deserialize(JsonParser jsonParser, DeserializationContext deserializationContext, Type type) { - return null; - } - } } diff --git a/examples/quarkus/base/pom.xml b/examples/quarkus/base/pom.xml index 139a58e753..ca03e65d3e 100644 --- a/examples/quarkus/base/pom.xml +++ b/examples/quarkus/base/pom.xml @@ -47,7 +47,7 @@ ${project.groupId} - blaze-persistence-integration-jaxrs + blaze-persistence-integration-jaxrs-jackson ${project.version} diff --git a/examples/spring-data-dgs/src/main/java/com/blazebit/persistence/examples/spring/data/dgs/GraphQLProvider.java b/examples/spring-data-dgs/src/main/java/com/blazebit/persistence/examples/spring/data/dgs/GraphQLProvider.java index 531b39c3d5..f53fcf8c8c 100644 --- a/examples/spring-data-dgs/src/main/java/com/blazebit/persistence/examples/spring/data/dgs/GraphQLProvider.java +++ b/examples/spring-data-dgs/src/main/java/com/blazebit/persistence/examples/spring/data/dgs/GraphQLProvider.java @@ -25,7 +25,6 @@ import com.netflix.graphql.dgs.DgsFederationResolver; import com.netflix.graphql.dgs.DgsTypeDefinitionRegistry; import com.netflix.graphql.dgs.autoconfig.DgsConfigurationProperties; -import com.netflix.graphql.dgs.internal.BaseDgsQueryExecutor; import com.netflix.graphql.dgs.internal.DgsSchemaProvider; import com.netflix.graphql.mocking.MockProvider; import graphql.execution.DataFetcherExceptionHandler; diff --git a/examples/spring-data-spqr/pom.xml b/examples/spring-data-spqr/pom.xml index d1a2ff7ea0..b5029c370b 100644 --- a/examples/spring-data-spqr/pom.xml +++ b/examples/spring-data-spqr/pom.xml @@ -86,12 +86,7 @@ ${project.groupId} - blaze-persistence-integration-jackson - ${project.version} - - - ${project.groupId} - blaze-persistence-integration-graphql + blaze-persistence-integration-graphql-spqr ${project.version} @@ -213,25 +208,23 @@ true - - 4.3.3.RELEASE - ${version.spring-data-1.x-spring-boot} + ${version.spring-data-2.2-spring} + ${version.spring-data-2.2-spring-boot} - org.springframework.data spring-data-jpa - ${version.spring-data-1.x} + ${version.spring-data-2.2} org.aspectj aspectjweaver - 1.8.9 + 1.9.4 ${project.groupId} - blaze-persistence-integration-spring-data-1.x + blaze-persistence-integration-spring-data-2.2 ${project.groupId} @@ -242,14 +235,14 @@ spring-data-2.0.x - ${version.spring-data-2.0-spring} - ${version.spring-data-2.0-spring-boot} + ${version.spring-data-2.2-spring} + ${version.spring-data-2.2-spring-boot} org.springframework.data spring-data-jpa - ${version.spring-data-2.0} + ${version.spring-data-2.2} org.aspectj @@ -258,7 +251,7 @@ ${project.groupId} - blaze-persistence-integration-spring-data-2.0 + blaze-persistence-integration-spring-data-2.2 ${project.groupId} @@ -269,14 +262,14 @@ spring-data-2.1.x - ${version.spring-data-2.1-spring} - ${version.spring-data-2.1-spring-boot} + ${version.spring-data-2.2-spring} + ${version.spring-data-2.2-spring-boot} org.springframework.data spring-data-jpa - ${version.spring-data-2.1} + ${version.spring-data-2.2} org.aspectj @@ -285,7 +278,7 @@ ${project.groupId} - blaze-persistence-integration-spring-data-2.1 + blaze-persistence-integration-spring-data-2.2 ${project.groupId} diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/Application.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/Application.java index c9e54fe5f7..be47812a3c 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/Application.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/Application.java @@ -33,7 +33,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @SpringBootApplication @EnableSpringDataWebSupport diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/BlazePersistenceConfiguration.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/BlazePersistenceConfiguration.java index 36b5fb2ef5..9072bea38b 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/BlazePersistenceConfiguration.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/BlazePersistenceConfiguration.java @@ -35,7 +35,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @Configuration @EnableEntityViews(basePackages = {"com.blazebit.persistence.examples.spring.data.spqr.view"}) diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/SpqrConfiguration.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/SpqrConfiguration.java index 431b22130a..0e4bfb290a 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/SpqrConfiguration.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/config/SpqrConfiguration.java @@ -18,32 +18,20 @@ import com.blazebit.persistence.integration.graphql.GraphQLEntityViewSupport; import com.blazebit.persistence.integration.graphql.GraphQLEntityViewSupportFactory; -import com.blazebit.persistence.integration.jackson.EntityViewAwareObjectMapper; import com.blazebit.persistence.view.EntityViewManager; -import com.fasterxml.jackson.databind.ObjectMapper; import graphql.schema.GraphQLSchema; -import io.leangen.graphql.metadata.strategy.DefaultInclusionStrategy; -import io.leangen.graphql.metadata.strategy.InclusionStrategy; -import io.leangen.graphql.metadata.strategy.InputFieldInclusionParams; -import io.leangen.graphql.metadata.strategy.value.ValueMapperFactory; -import io.leangen.graphql.metadata.strategy.value.jackson.JacksonValueMapperFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Scope; import javax.annotation.PostConstruct; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Map; /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @Configuration public class SpqrConfiguration { @@ -69,40 +57,4 @@ public void init() { public GraphQLEntityViewSupport graphQLEntityViewSupport() { return graphQLEntityViewSupport; } - - @Bean - public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { - return builder -> builder.postConfigurer(objectMapper -> new EntityViewAwareObjectMapper(evm, objectMapper)); - } - - @Bean - public ValueMapperFactory valueMapperFactory(ObjectMapper objectMapper) { - return JacksonValueMapperFactory.builder() - .withPrototype(objectMapper) - .build(); - } - - @Bean - public InclusionStrategy inclusionStrategy() { - return new DefaultInclusionStrategy() { - @Override - public boolean includeInputField(InputFieldInclusionParams params) { - if (params.getElements().stream().noneMatch(this::isIgnored) && isPackageAcceptable(params.getDeclaringType(), params.getElementDeclaringClass())) { - if (params.isDirectlyDeserializable() || params.isDeserializableInSubType()) { - return true; - } - // Always include collections even if there is no setter available - for (AnnotatedElement element : params.getElements()) { - if (element instanceof Method) { - Class returnType = ((Method) element).getReturnType(); - if (Collection.class.isAssignableFrom(returnType) || Map.class.isAssignableFrom(returnType)) { - return true; - } - } - } - } - return false; - } - }; - } } \ No newline at end of file diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Cat.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Cat.java index 30d2d412a4..70701496c2 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Cat.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Cat.java @@ -30,7 +30,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @Entity public class Cat { diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Person.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Person.java index 3feb2aee93..a71cc6003f 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Person.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/model/Person.java @@ -25,7 +25,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @Entity public class Person { diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatJpaRepository.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatJpaRepository.java index 5d21399d35..050ca5ae8b 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatJpaRepository.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatJpaRepository.java @@ -21,7 +21,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ public interface CatJpaRepository extends JpaRepository { diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatViewRepository.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatViewRepository.java index 70854df81e..68b819bd89 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatViewRepository.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/repository/CatViewRepository.java @@ -29,7 +29,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @Component public class CatViewRepository { diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/resource/CatGraphQLApi.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/resource/CatGraphQLApi.java index dff0e37681..09713a80f7 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/resource/CatGraphQLApi.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/resource/CatGraphQLApi.java @@ -31,7 +31,6 @@ import io.leangen.graphql.execution.ResolutionEnvironment; import io.leangen.graphql.execution.relay.Page; import io.leangen.graphql.execution.relay.generic.GenericPage; -import io.leangen.graphql.execution.relay.generic.PageFactory; import io.leangen.graphql.spqr.spring.annotations.GraphQLApi; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -39,7 +38,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @Component @GraphQLApi diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatCreateView.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatCreateView.java index d1a1eb7650..087e7dafcd 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatCreateView.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatCreateView.java @@ -24,7 +24,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @CreatableEntityView @EntityView(Cat.class) diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleCreateView.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleCreateView.java index bde1c5c698..f27ee885c8 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleCreateView.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleCreateView.java @@ -22,7 +22,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @CreatableEntityView @EntityView(Cat.class) diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleView.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleView.java index 22218135e0..c34c2cd1cd 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleView.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatSimpleView.java @@ -22,7 +22,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @EntityView(Cat.class) public interface CatSimpleView { diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatUpdateView.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatUpdateView.java index 4b241ad46f..2ce4e083e3 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatUpdateView.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatUpdateView.java @@ -22,7 +22,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @UpdatableEntityView @EntityView(Cat.class) diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatWithOwnerView.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatWithOwnerView.java index 5eb675f4f5..f41d710d3b 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatWithOwnerView.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/CatWithOwnerView.java @@ -21,7 +21,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @EntityView(Cat.class) public interface CatWithOwnerView extends CatSimpleView { diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonIdView.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonIdView.java index b086e753e1..af56766f14 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonIdView.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonIdView.java @@ -22,7 +22,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @EntityView(Person.class) public interface PersonIdView { diff --git a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonSimpleView.java b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonSimpleView.java index d13bbff936..b37c1f36e1 100644 --- a/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonSimpleView.java +++ b/examples/spring-data-spqr/src/main/java/com/blazebit/persistence/examples/spring/data/spqr/view/PersonSimpleView.java @@ -21,7 +21,7 @@ /** * @author Christian Beikov - * @since 1.4.0 + * @since 1.6.4 */ @EntityView(Person.class) public interface PersonSimpleView extends PersonIdView { diff --git a/integration/deltaspike-data/rest/impl/pom.xml b/integration/deltaspike-data/rest/impl/pom.xml index 618793307d..de6fce9996 100644 --- a/integration/deltaspike-data/rest/impl/pom.xml +++ b/integration/deltaspike-data/rest/impl/pom.xml @@ -28,6 +28,10 @@ ${project.groupId} blaze-persistence-integration-jaxrs + + + ${project.groupId} + blaze-persistence-integration-jaxrs-jackson runtime diff --git a/integration/graphql-spqr-jakarta/pom.xml b/integration/graphql-spqr-jakarta/pom.xml new file mode 100644 index 0000000000..8bf3524fc3 --- /dev/null +++ b/integration/graphql-spqr-jakarta/pom.xml @@ -0,0 +1,171 @@ + + + + + 4.0.0 + + + com.blazebit + blaze-persistence-integration + 1.6.4-SNAPSHOT + ../pom.xml + + + blaze-persistence-integration-graphql-spqr-jakarta + jar + + + + ${project.groupId} + blaze-persistence-integration-graphql-spqr + provided + + + jakarta.persistence + jakarta.persistence-api + ${version.jakarta-jpa-api} + + + ${project.groupId} + blaze-persistence-integration-graphql-jakarta + + + ${project.groupId} + blaze-persistence-integration-jackson-jakarta + + + ${project.groupId} + blaze-persistence-entity-view-api-jakarta + + + + + + + maven-antrun-plugin + + + transform-jar + package + + run + + + + + + + + + + + + + + + + + + + + + + transform-sources-jar + package + + run + + + + + + + + + + + + + + + + + + + + + + + + transform-javadoc + package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + org.eclipse.transformer + org.eclipse.transformer.cli + 0.2.0 + + + ant-contrib + ant-contrib + 1.0b3 + + + ant + ant + + + + + + + + + diff --git a/integration/graphql-spqr/pom.xml b/integration/graphql-spqr/pom.xml new file mode 100644 index 0000000000..a4cdde2d70 --- /dev/null +++ b/integration/graphql-spqr/pom.xml @@ -0,0 +1,215 @@ + + + + blaze-persistence-integration + com.blazebit + 1.6.4-SNAPSHOT + ../pom.xml + + 4.0.0 + + blaze-persistence-integration-graphql-spqr + + Blazebit Persistence Integration GraphQL SPQR + + + com.blazebit.persistence.integration.graphql.spqr + 1.8 + 1.8 + ${version.spring-data-2.4-spring} + ${version.spring-data-2.4-spring-boot} + ${version.hibernate-5.4} + + + + + ${project.groupId} + blaze-persistence-integration-graphql + + + ${project.groupId} + blaze-persistence-integration-jackson + + + ${project.groupId} + blaze-persistence-entity-view-api + + + com.graphql-java + graphql-java + 16.2 + provided + + + com.fasterxml.jackson.core + jackson-core + 2.12.1 + provided + + + org.springframework.boot + spring-boot-starter-web + ${version.spring.boot} + provided + + + io.leangen.graphql + graphql-spqr-spring-boot-starter + 0.0.6 + provided + + + org.springframework + spring-context + ${version.spring} + provided + + + + ${project.groupId} + blaze-persistence-core-impl + test + + + ${project.groupId} + blaze-persistence-entity-view-impl + test + + + + org.springframework.boot + spring-boot-starter-tomcat + ${version.spring.boot} + test + + + org.springframework.boot + spring-boot-starter-test + ${version.spring.boot} + test + + + org.springframework.boot + spring-boot-starter-data-jpa + ${version.spring.boot} + test + + + org.hibernate + hibernate-core + + + + + org.hibernate + hibernate-entitymanager + ${version.hibernate} + test + + + + + org.springframework.data + spring-data-jpa + ${version.spring-data-2.4} + test + + + org.aspectj + aspectjweaver + 1.9.4 + test + + + ${project.groupId} + blaze-persistence-integration-spring-data-2.4 + test + + + ${project.groupId} + blaze-persistence-integration-hibernate-5.4 + test + + + + + junit + junit + test + + + com.h2database + h2 + test + + + org.springframework + spring-test + ${version.spring} + test + + + + + jakarta.xml.bind + jakarta.xml.bind-api + ${version.jaxb-api} + test + + + com.sun.xml.bind + jaxb-impl + ${version.jaxb} + test + + + jakarta.annotation + jakarta.annotation-api + ${version.annotation} + test + + + + + + + org.jboss.jandex + jandex-maven-plugin + 1.0.8 + + + make-index + + jandex + + + + + + org.moditect + moditect-maven-plugin + + + add-module-infos + package + + add-module-info + + + + + module ${module.name} { + requires transitive com.blazebit.persistence.view; + requires com.blazebit.common.utils; + exports com.blazebit.persistence.integration.graphql; + } + + + + + + + + + + \ No newline at end of file diff --git a/integration/graphql-spqr/src/main/java/com/blazebit/persistence/integration/graphql/spqr/BlazePersistenceSpqrAutoConfiguration.java b/integration/graphql-spqr/src/main/java/com/blazebit/persistence/integration/graphql/spqr/BlazePersistenceSpqrAutoConfiguration.java new file mode 100644 index 0000000000..9a58d3fafa --- /dev/null +++ b/integration/graphql-spqr/src/main/java/com/blazebit/persistence/integration/graphql/spqr/BlazePersistenceSpqrAutoConfiguration.java @@ -0,0 +1,99 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr; + +import com.blazebit.persistence.integration.jackson.EntityViewAwareObjectMapper; +import com.blazebit.persistence.view.EntityViewManager; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.leangen.graphql.metadata.strategy.DefaultInclusionStrategy; +import io.leangen.graphql.metadata.strategy.InclusionStrategy; +import io.leangen.graphql.metadata.strategy.InputFieldInclusionParams; +import io.leangen.graphql.metadata.strategy.value.ValueMapperFactory; +import io.leangen.graphql.metadata.strategy.value.jackson.JacksonValueMapperFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Configuration +public class BlazePersistenceSpqrAutoConfiguration { + + @Bean + public Jackson2ObjectMapperBuilderCustomizer blazePersistenceJsonCustomizer(EntityViewManager evm) { + Module module = new Module() { + @Override + public String getModuleName() { + return "com.blazebit.persistence"; + } + + @Override + public Version version() { + return Version.unknownVersion(); + } + + @Override + public void setupModule(SetupContext context) { + new EntityViewAwareObjectMapper(evm, context.getOwner()); + } + }; + return builder -> builder.modules(module); + } + + @Bean + @ConditionalOnMissingBean + public ValueMapperFactory valueMapperFactory(ObjectMapper objectMapper) { + return JacksonValueMapperFactory.builder() + .withPrototype(objectMapper) + .build(); + } + + @Bean + @ConditionalOnMissingBean + public InclusionStrategy inclusionStrategy() { + return new DefaultInclusionStrategy() { + @Override + public boolean includeInputField(InputFieldInclusionParams params) { + if (params.getElements().stream().noneMatch(this::isIgnored) && isPackageAcceptable(params.getDeclaringType(), params.getElementDeclaringClass())) { + if (params.isDirectlyDeserializable() || params.isDeserializableInSubType()) { + return true; + } + // Always include collections even if there is no setter available + for (AnnotatedElement element : params.getElements()) { + if (element instanceof Method) { + Class returnType = ((Method) element).getReturnType(); + if (Collection.class.isAssignableFrom(returnType) || Map.class.isAssignableFrom(returnType)) { + return true; + } + } + } + } + return false; + } + }; + } +} \ No newline at end of file diff --git a/integration/graphql-spqr/src/main/resources/META-INF/spring.factories b/integration/graphql-spqr/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..d5b9968d83 --- /dev/null +++ b/integration/graphql-spqr/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.blazebit.persistence.integration.graphql.spqr.BlazePersistenceSpqrAutoConfiguration \ No newline at end of file diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/AbstractSampleTest.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/AbstractSampleTest.java new file mode 100644 index 0000000000..074e474ace --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/AbstractSampleTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import com.blazebit.persistence.integration.graphql.spqr.model.Person; +import org.junit.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +public abstract class AbstractSampleTest { + + @Autowired + DataInitializer dataInitializer; + + @Before + public void init() { + dataInitializer.run(em -> { + ThreadLocalRandom random = ThreadLocalRandom.current(); + List people = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + Person p = new Person("Person " + i); + people.add(p); + em.persist(p); + } + for (int i = 0; i < 100; i++) { + Cat c = new Cat("Cat " + i, random.nextInt(20), people.get(random.nextInt(4))); + em.persist(c); + } + }); + } + + @Transactional + @Component + public static class DataInitializer { + + @Autowired + EntityManager em; + + public void run(Consumer c) { + c.accept(em); + } + } +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/Application.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/Application.java new file mode 100644 index 0000000000..a1d8d43f2b --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/Application.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import com.blazebit.persistence.integration.graphql.spqr.model.Person; +import com.blazebit.persistence.integration.graphql.spqr.repository.CatJpaRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.web.config.EnableSpringDataWebSupport; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author Christian Beikov + * @since 1.4.0 + */ +@SpringBootApplication +@EnableSpringDataWebSupport +@ComponentScan(basePackages = "com.blazebit.persistence.integration.graphql.spqr") +public class Application { + + @Autowired + EntityManager em; + @Autowired + CatJpaRepository catJpaRepository; + + public static void main(String[] args) { + SpringApplication.run(Application.class).getBean(Application.class).run(); + } + + @Transactional + public void run() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + List people = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + Person p = new Person("Person " + i); + people.add(p); + em.persist(p); + } + for (int i = 0; i < 100; i++) { + Cat c = new Cat("Cat " + i, random.nextInt(20), people.get(random.nextInt(4))); + catJpaRepository.save(c); + } + } +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/SampleTest.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/SampleTest.java new file mode 100644 index 0000000000..70be893915 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/SampleTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class SampleTest extends AbstractSampleTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void testSanity() { + this.restTemplate.getForObject("/graphql/schema.json", String.class); + } + + @Test + public void testRequestScope() { + String requestGraphQL = request(5, null); + HttpHeaders headers = new HttpHeaders(); + headers.add("content-type", "application/graphql"); + ResponseEntity response = this.restTemplate.postForEntity("/graphql", new HttpEntity<>(requestGraphQL, headers), JsonNode.class); + + JsonNode connection = response.getBody().get("data").get("findAll"); + ArrayNode arrayNode = (ArrayNode) connection.get("edges"); + List nodes = arrayNode.findValues("node"); + + assertEquals(5, nodes.size()); + assertEquals("Cat 0", nodes.get(0).get("name").asText()); + + requestGraphQL = request(5, connection.get("pageInfo").get("endCursor").asText()); + response = this.restTemplate.postForEntity("/graphql", new HttpEntity<>(requestGraphQL, headers), JsonNode.class); + connection = response.getBody().get("data").get("findAll"); + arrayNode = (ArrayNode) connection.get("edges"); + nodes = arrayNode.findValues("node"); + + assertEquals(5, nodes.size()); + assertEquals("Cat 5", nodes.get(0).get("name").asText()); + } + + @Test + public void testCreate() { + String requestGraphQL = "mutation {\n" + + " createCat(\n" + + " cat: {\n" + + " name: \"Test\"\n" + + " age: 1\n" + + " owner: {id: 1}\n" + + " kittens: [\n" + + " { name: \"Kitten 1\", age: 1, owner: {id: 1}}\n" + + " ]\n" + + " \t}\n" + + " )\n" + + "}"; + HttpHeaders headers = new HttpHeaders(); + headers.add("content-type", "application/graphql"); + ResponseEntity response = this.restTemplate.postForEntity("/graphql", new HttpEntity<>(requestGraphQL, headers), JsonNode.class); + + int id = response.getBody().get("data").get("createCat").asInt(); + + requestGraphQL = "query { catById(id: " + id + ") { name } }"; + response = this.restTemplate.postForEntity("/graphql", new HttpEntity<>(requestGraphQL, headers), JsonNode.class); + String name = response.getBody().get("data").get("catById").get("name").asText(); + assertEquals("Test", name); + } + + static String request(int first, String after) { + String other = ""; + if (after != null) { + other = ", after: \"" + after + "\""; + } + String requestGraphQL = "query {\n" + + " findAll(first: " + first + other + "){\n" + + " edges {\n" + + " node {\n" + + " id\n" + + " name\n" + + " }\n" + + " }\n" + + " pageInfo {\n" + + " startCursor\n" + + " endCursor\n" + + " }\n" + + " }\n" + + "}"; + return requestGraphQL; + } +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/BlazePersistenceConfiguration.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/BlazePersistenceConfiguration.java new file mode 100644 index 0000000000..8e1a7dbb65 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/BlazePersistenceConfiguration.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.config; + +import com.blazebit.persistence.Criteria; +import com.blazebit.persistence.CriteriaBuilderFactory; +import com.blazebit.persistence.integration.view.spring.EnableEntityViews; +import com.blazebit.persistence.spi.CriteriaBuilderConfiguration; +import com.blazebit.persistence.spring.data.impl.repository.BlazePersistenceRepositoryFactoryBean; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Scope; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceUnit; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Configuration +@EnableEntityViews(basePackages = {"com.blazebit.persistence.integration.graphql.spqr.view"}) +@EnableJpaRepositories( + basePackages = "com.blazebit.persistence.integration.graphql.spqr.repository", + repositoryFactoryBeanClass = BlazePersistenceRepositoryFactoryBean.class) +public class BlazePersistenceConfiguration { + + @PersistenceUnit + private EntityManagerFactory entityManagerFactory; + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) + @Lazy(false) + public CriteriaBuilderFactory createCriteriaBuilderFactory() { + CriteriaBuilderConfiguration config = Criteria.getDefault(); + return config.createCriteriaBuilderFactory(entityManagerFactory); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) + @Lazy(false) + public EntityViewManager createEntityViewManager(CriteriaBuilderFactory cbf, EntityViewConfiguration entityViewConfiguration) { + return entityViewConfiguration.createEntityViewManager(cbf); + } +} \ No newline at end of file diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/SpqrConfiguration.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/SpqrConfiguration.java new file mode 100644 index 0000000000..0b263ef3c5 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/config/SpqrConfiguration.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.config; + +import com.blazebit.persistence.integration.graphql.GraphQLEntityViewSupport; +import com.blazebit.persistence.integration.graphql.GraphQLEntityViewSupportFactory; +import com.blazebit.persistence.integration.jackson.EntityViewAwareObjectMapper; +import com.blazebit.persistence.view.EntityViewManager; +import com.fasterxml.jackson.databind.ObjectMapper; +import graphql.schema.GraphQLSchema; +import io.leangen.graphql.metadata.strategy.DefaultInclusionStrategy; +import io.leangen.graphql.metadata.strategy.InclusionStrategy; +import io.leangen.graphql.metadata.strategy.InputFieldInclusionParams; +import io.leangen.graphql.metadata.strategy.value.ValueMapperFactory; +import io.leangen.graphql.metadata.strategy.value.jackson.JacksonValueMapperFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Scope; + +import javax.annotation.PostConstruct; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Configuration +public class SpqrConfiguration { + + @Autowired + EntityViewManager evm; + @Autowired + GraphQLSchema graphQLSchema; + + private GraphQLEntityViewSupport graphQLEntityViewSupport; + + @PostConstruct + public void init() { + GraphQLEntityViewSupportFactory graphQLEntityViewSupportFactory = new GraphQLEntityViewSupportFactory(false, false); + graphQLEntityViewSupportFactory.setImplementRelayNode(false); + graphQLEntityViewSupportFactory.setDefineRelayNodeIfNotExist(false); + this.graphQLEntityViewSupport = graphQLEntityViewSupportFactory.create(graphQLSchema, evm); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) + @Lazy(false) + public GraphQLEntityViewSupport graphQLEntityViewSupport() { + return graphQLEntityViewSupport; + } +} \ No newline at end of file diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Cat.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Cat.java new file mode 100644 index 0000000000..19bbc1602e --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Cat.java @@ -0,0 +1,118 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Entity +public class Cat { + + @Id + @GeneratedValue + private Long id; + private String name; + private Integer age; + @JsonIgnoreProperties("kittens") + @ManyToOne(fetch = FetchType.LAZY, optional = true) + private Person owner; + @ManyToOne(fetch = FetchType.LAZY, optional = true) + private Cat mother; + @ManyToOne(fetch = FetchType.LAZY, optional = true) + private Cat father; + @JsonIgnore + @ManyToMany + private Set kittens = new HashSet<>(); + + public Cat() { + } + + public Cat(String name, Integer age, Person owner) { + this.name = name; + this.age = age; + this.owner = owner; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Person getOwner() { + return owner; + } + + public void setOwner(Person owner) { + this.owner = owner; + } + + public Cat getMother() { + return mother; + } + + public void setMother(Cat mother) { + this.mother = mother; + } + + public Cat getFather() { + return father; + } + + public void setFather(Cat father) { + this.father = father; + } + + public Set getKittens() { + return kittens; + } + + public void setKittens(Set kittens) { + this.kittens = kittens; + } +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Person.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Person.java new file mode 100644 index 0000000000..c49099f4f2 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/model/Person.java @@ -0,0 +1,70 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Entity +public class Person { + + @Id + @GeneratedValue + private Long id; + private String name; + @OneToMany(mappedBy = "owner") + private Set kittens = new HashSet<>(); + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getKittens() { + return kittens; + } + + public void setKittens(Set kittens) { + this.kittens = kittens; + } +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatJpaRepository.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatJpaRepository.java new file mode 100644 index 0000000000..81dd20621a --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatJpaRepository.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.repository; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +public interface CatJpaRepository extends JpaRepository { + +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatViewRepository.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatViewRepository.java new file mode 100644 index 0000000000..25067c7d3e --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/repository/CatViewRepository.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.repository; + +import com.blazebit.persistence.CriteriaBuilder; +import com.blazebit.persistence.CriteriaBuilderFactory; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.util.List; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Component +public class CatViewRepository { + + @Autowired + EntityManager em; + @Autowired + CriteriaBuilderFactory cbf; + @Autowired + EntityViewManager evm; + + public T findById(EntityViewSetting> setting, Long id) { + return evm.find(em, setting, id); + } + + public List findAll(EntityViewSetting setting) { + return evm.applySetting(setting, cbf.create(em, evm.getMetamodel().managedView(setting.getEntityViewClass()).getEntityClass())).getResultList(); + } + + @Transactional + public void save(Object o) { + evm.save(em, o); + } +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/resource/CatGraphQLApi.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/resource/CatGraphQLApi.java new file mode 100644 index 0000000000..ab10d3b9de --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/resource/CatGraphQLApi.java @@ -0,0 +1,79 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.resource; + +import com.blazebit.persistence.integration.graphql.GraphQLEntityViewSupport; +import com.blazebit.persistence.integration.graphql.spqr.repository.CatViewRepository; +import com.blazebit.persistence.integration.graphql.spqr.view.CatCreateView; +import com.blazebit.persistence.integration.graphql.spqr.view.CatWithOwnerView; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import com.blazebit.persistence.view.Sorters; +import graphql.relay.Connection; +import io.leangen.graphql.annotations.GraphQLArgument; +import io.leangen.graphql.annotations.GraphQLEnvironment; +import io.leangen.graphql.annotations.GraphQLMutation; +import io.leangen.graphql.annotations.GraphQLQuery; +import io.leangen.graphql.execution.ResolutionEnvironment; +import io.leangen.graphql.execution.relay.Page; +import io.leangen.graphql.execution.relay.generic.GenericPage; +import io.leangen.graphql.spqr.spring.annotations.GraphQLApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Component +@GraphQLApi +public class CatGraphQLApi { + + @Autowired + EntityViewManager evm; + @Autowired + CatViewRepository repository; + @Autowired + @Lazy + GraphQLEntityViewSupport graphQLEntityViewSupport; + + @GraphQLQuery + public CatWithOwnerView catById(@GraphQLArgument(name = "id") Long id, @GraphQLEnvironment ResolutionEnvironment env) { + return repository.findById(graphQLEntityViewSupport.createSetting(env.dataFetchingEnvironment), id); + } + + @GraphQLQuery + public Page findAll( + @GraphQLArgument(name = "first") Integer first, + @GraphQLArgument(name = "last") Integer last, + @GraphQLArgument(name = "offset") Integer offset, + @GraphQLArgument(name = "before") String before, + @GraphQLArgument(name = "after") String after, + @GraphQLEnvironment ResolutionEnvironment env) { + EntityViewSetting setting = graphQLEntityViewSupport.createPaginatedSetting(env.dataFetchingEnvironment, first, last, offset, before, after); + setting.addAttributeSorter("id", Sorters.ascending()); + Connection relayConnection = graphQLEntityViewSupport.createRelayConnection(repository.findAll(setting)); + return new GenericPage<>(relayConnection.getEdges(), relayConnection.getPageInfo()); + } + + @GraphQLMutation + public Long createCat(@GraphQLArgument(name = "cat") CatCreateView cat) { + repository.save(cat); + return cat.getId(); + } +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatCreateView.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatCreateView.java new file mode 100644 index 0000000000..1da7e6a99b --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatCreateView.java @@ -0,0 +1,35 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.view; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import com.blazebit.persistence.view.CreatableEntityView; +import com.blazebit.persistence.view.EntityView; + +import java.util.Set; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@CreatableEntityView +@EntityView(Cat.class) +public interface CatCreateView extends CatSimpleCreateView { + + Set getKittens(); + void setKittens(Set kittens); +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleCreateView.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleCreateView.java new file mode 100644 index 0000000000..7b9d50fe21 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleCreateView.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.view; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import com.blazebit.persistence.view.CreatableEntityView; +import com.blazebit.persistence.view.EntityView; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@CreatableEntityView +@EntityView(Cat.class) +public interface CatSimpleCreateView extends CatUpdateView { + + PersonIdView getOwner(); + + void setOwner(PersonIdView owner); +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleView.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleView.java new file mode 100644 index 0000000000..421b1eb23c --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatSimpleView.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.view; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@EntityView(Cat.class) +public interface CatSimpleView { + + @IdMapping + Long getId(); + + String getName(); +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatUpdateView.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatUpdateView.java new file mode 100644 index 0000000000..aef01d04d7 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatUpdateView.java @@ -0,0 +1,36 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.view; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.UpdatableEntityView; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@UpdatableEntityView +@EntityView(Cat.class) +public interface CatUpdateView extends CatSimpleView { + + void setName(String name); + + Integer getAge(); + + void setAge(Integer age); +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatWithOwnerView.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatWithOwnerView.java new file mode 100644 index 0000000000..f831bf6a61 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/CatWithOwnerView.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.view; + +import com.blazebit.persistence.integration.graphql.spqr.model.Cat; +import com.blazebit.persistence.view.EntityView; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@EntityView(Cat.class) +public interface CatWithOwnerView extends CatSimpleView { + + PersonSimpleView getOwner(); + +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonIdView.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonIdView.java new file mode 100644 index 0000000000..6493efd6ac --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonIdView.java @@ -0,0 +1,35 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.view; + +import com.blazebit.persistence.integration.graphql.spqr.model.Person; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@EntityView(Person.class) +public interface PersonIdView { + + @IdMapping + Long getId(); + + void setId(Long id); + +} diff --git a/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonSimpleView.java b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonSimpleView.java new file mode 100644 index 0000000000..bb6267c530 --- /dev/null +++ b/integration/graphql-spqr/src/test/java/com/blazebit/persistence/integration/graphql/spqr/view/PersonSimpleView.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.graphql.spqr.view; + +import com.blazebit.persistence.integration.graphql.spqr.model.Person; +import com.blazebit.persistence.view.EntityView; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@EntityView(Person.class) +public interface PersonSimpleView extends PersonIdView { + + String getName(); + +} diff --git a/integration/graphql-spqr/src/test/resources/META-INF/test-config.xml b/integration/graphql-spqr/src/test/resources/META-INF/test-config.xml new file mode 100644 index 0000000000..1a0d678e21 --- /dev/null +++ b/integration/graphql-spqr/src/test/resources/META-INF/test-config.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/integration/graphql-spqr/src/test/resources/application.properties b/integration/graphql-spqr/src/test/resources/application.properties new file mode 100644 index 0000000000..4ab235c742 --- /dev/null +++ b/integration/graphql-spqr/src/test/resources/application.properties @@ -0,0 +1,8 @@ +server.contextPath=/ +server.port=8080 +graphql.spqr.gui.enabled=true +spring.h2.console.enabled=true +spring.jpa.show-sql=true +#logging.level.org.hibernate.SQL=DEBUG +spring.jpa.hibernate.ddl-auto=create-drop +spring.jackson.serialization.fail-on-empty-beans=false \ No newline at end of file diff --git a/integration/graphql-spqr/src/test/resources/logback-test.xml b/integration/graphql-spqr/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..15ed935c18 --- /dev/null +++ b/integration/graphql-spqr/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/integration/graphql/src/main/java/com/blazebit/persistence/integration/graphql/GraphQLEntityViewSupportFactory.java b/integration/graphql/src/main/java/com/blazebit/persistence/integration/graphql/GraphQLEntityViewSupportFactory.java index b9f7661d64..e10156b66b 100644 --- a/integration/graphql/src/main/java/com/blazebit/persistence/integration/graphql/GraphQLEntityViewSupportFactory.java +++ b/integration/graphql/src/main/java/com/blazebit/persistence/integration/graphql/GraphQLEntityViewSupportFactory.java @@ -63,6 +63,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; @@ -535,10 +536,7 @@ public GraphQLEntityViewSupport create(GraphQLSchema.Builder schemaBuilder, Enti continue; } String typeName = getObjectTypeName(managedView); - String inputTypeName = typeName + "Input"; - if (managedView.isCreatable() || managedView.isUpdatable()) { - inputTypeName = typeName; - } + String inputTypeName = getInputObjectTypeName(managedView); String description = getDescription(managedView.getJavaType()); GraphQLObjectType.Builder builder = GraphQLObjectType.newObject().name(typeName); GraphQLInputObjectType.Builder inputBuilder = GraphQLInputObjectType.newInputObject().name(inputTypeName); @@ -817,16 +815,14 @@ protected void addObjectTypeDefinition(TypeDefinitionRegistry typeRegistry, Map< } protected void addObjectTypeDefinition(GraphQLSchema.Builder schemaBuilder, Map> typeNameToClass, ManagedViewType managedView, GraphQLObjectType objectType, GraphQLInputObjectType inputObjectType) { - if (!managedView.isUpdatable() && !managedView.isCreatable()) { - typeNameToClass.put(objectType.getName(), managedView.getJavaType()); - if (isDefineNormalTypes()) { - schemaBuilder.additionalType(objectType); - } - } typeNameToClass.put(inputObjectType.getName(), managedView.getJavaType()); if (isDefineNormalTypes()) { schemaBuilder.additionalType(inputObjectType); } + if (managedView.isUpdatable() || managedView.isCreatable()) { + // No need to define relay types for creatable/updatable views + return; + } String nodeTypeName; String edgeTypeName; String connectionTypeName; @@ -901,9 +897,13 @@ protected void addObjectTypeDefinition(GraphQLSchema.Builder schemaBuilder, Map< schemaBuilder.additionalType(pageInfoType.build()); typeNameToClass.put(pageInfoTypeName, GraphQLRelayPageInfo.class); } - typeNameToClass.put(edgeTypeName, managedView.getJavaType()); typeNameToClass.put(connectionTypeName, managedView.getJavaType()); + typeNameToClass.put(objectType.getName(), managedView.getJavaType()); + if (isDefineNormalTypes()) { + schemaBuilder.additionalType(objectType); + } + if (isDefineRelayTypes()) { schemaBuilder.additionalType(edgeType.build()); schemaBuilder.additionalType(connectionType.build()); @@ -1083,6 +1083,24 @@ protected String getObjectTypeName(ManagedViewType type) { return type.getJavaType().getSimpleName(); } + /** + * Returns the GraphQL input type name for the given managed view type. + * + * @param managedView The managed view type + * @return The GraphQL type name + */ + protected String getInputObjectTypeName(ManagedViewType managedView) { + String typeName = getObjectTypeName(managedView); + // So far, we only use this for MicroProfile GraphQL where we can't register custom types + // and instead have to simply use the name the MP GraphQL implementations choose for such types. + // In case of input object types, implementations don't suffix the name with "Input" since the type is abstract + if (Modifier.isAbstract(managedView.getJavaType().getModifiers()) && (managedView.isCreatable() || managedView.isUpdatable())) { + return typeName; + } else { + return typeName + "Input"; + } + } + /** * Returns the GraphQL type name for the given java type. * @@ -1210,10 +1228,7 @@ protected GraphQLOutputType getObjectTypeReference(ManagedViewType type) { * @return The type */ protected GraphQLInputType getInputObjectTypeReference(ManagedViewType type) { - if (type.isCreatable() || type.isUpdatable()) { - return new GraphQLTypeReference(getObjectTypeName(type)); - } - return new GraphQLTypeReference(getObjectTypeName(type) + "Input"); + return new GraphQLTypeReference(getInputObjectTypeName(type)); } /** diff --git a/integration/jackson/src/main/java/com/blazebit/persistence/integration/jackson/EntityViewAwareObjectMapper.java b/integration/jackson/src/main/java/com/blazebit/persistence/integration/jackson/EntityViewAwareObjectMapper.java index a645acbf51..cf2b5e7424 100644 --- a/integration/jackson/src/main/java/com/blazebit/persistence/integration/jackson/EntityViewAwareObjectMapper.java +++ b/integration/jackson/src/main/java/com/blazebit/persistence/integration/jackson/EntityViewAwareObjectMapper.java @@ -20,7 +20,6 @@ import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.MethodAttribute; import com.blazebit.persistence.view.metamodel.ViewMetamodel; -import com.blazebit.persistence.view.metamodel.ViewType; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; @@ -30,7 +29,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; -import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; import com.fasterxml.jackson.databind.introspect.VisibilityChecker; import com.fasterxml.jackson.databind.module.SimpleModule; diff --git a/integration/jaxrs-jakarta/pom.xml b/integration/jaxrs-jackson-jakarta/pom.xml similarity index 96% rename from integration/jaxrs-jakarta/pom.xml rename to integration/jaxrs-jackson-jakarta/pom.xml index 10b37d6529..ecab26a08d 100644 --- a/integration/jaxrs-jakarta/pom.xml +++ b/integration/jaxrs-jackson-jakarta/pom.xml @@ -25,13 +25,13 @@ ../pom.xml - blaze-persistence-integration-jaxrs-jakarta + blaze-persistence-integration-jaxrs-jackson-jakarta jar ${project.groupId} - blaze-persistence-integration-jaxrs + blaze-persistence-integration-jaxrs-jackson provided @@ -69,7 +69,7 @@ - + @@ -94,7 +94,7 @@ @@ -125,7 +125,7 @@ diff --git a/integration/jaxrs-jackson/pom.xml b/integration/jaxrs-jackson/pom.xml new file mode 100644 index 0000000000..26f8ae5686 --- /dev/null +++ b/integration/jaxrs-jackson/pom.xml @@ -0,0 +1,253 @@ + + + + blaze-persistence-integration + com.blazebit + 1.6.4-SNAPSHOT + ../pom.xml + + 4.0.0 + + blaze-persistence-integration-jaxrs-jackson + + Blazebit Persistence Integration JAX-RS Jackson + + + com.blazebit.persistence.integration.jaxrs.jackson + + UTF-8 + 1.8 + 1.8 + + 1.9.3 + 5.4.6.Final + 5.4 + 2.30.1 + 2.9.8 + 2.1.1 + + + + + + org.apache.deltaspike.distribution + distributions-bom + ${version.deltaspike} + pom + import + + + + + + + ${project.groupId} + blaze-persistence-entity-view-api + + + com.fasterxml.jackson.core + jackson-databind + ${version.jackson} + provided + + + ${project.groupId} + blaze-persistence-integration-jackson + + + ${project.groupId} + blaze-persistence-integration-jaxrs + + + ${project.groupId} + blaze-common-utils + + + javax.ws.rs + javax.ws.rs-api + ${version.jaxrs} + provided + + + org.glassfish.jersey.core + jersey-common + ${version.jersey} + true + + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.0.Final + provided + + + javax.enterprise + cdi-api + 2.0.SP1 + provided + + + jakarta.annotation + jakarta.annotation-api + ${version.annotation} + provided + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${version.jackson} + test + + + com.blazebit + blaze-persistence-integration-entity-view-cdi + test + + + com.blazebit + blaze-persistence-core-impl + test + + + com.blazebit + blaze-persistence-entity-view-impl + test + + + ${project.groupId} + blaze-persistence-testsuite-base-jpa + test + + + com.blazebit + blaze-persistence-integration-hibernate-${version.integration-hibernate} + test + + + org.hibernate + hibernate-entitymanager + ${version.hibernate} + test + + + org.apache.deltaspike.modules + deltaspike-jpa-module-impl + test + + + org.glassfish.jersey.core + jersey-server + ${version.jersey} + test + + + org.glassfish.jersey.containers + jersey-container-jetty-http + ${version.jersey} + test + + + org.glassfish.jersey.inject + jersey-cdi2-se + ${version.jersey} + test + + + org.jboss + jandex + + + javax.enterprise + cdi-api + + + + + com.h2database + h2 + test + + + junit + junit + test + + + + + jakarta.xml.bind + jakarta.xml.bind-api + ${version.jaxb-api} + test + + + com.sun.xml.bind + jaxb-impl + ${version.jaxb} + test + + + + + + + org.moditect + moditect-maven-plugin + + + add-module-infos + package + + add-module-info + + + + + module ${module.name} { + requires transitive com.blazebit.persistence.view; + requires com.blazebit.common.utils; + requires com.blazebit.persistence.integration.jackson; + exports com.blazebit.persistence.integration.jaxrs; + provides org.glassfish.jersey.model.internal.spi.ParameterServiceProvider with com.blazebit.persistence.integration.jaxrs.jackson.jersey.EntityViewIdAwareParameterServiceProvider; + } + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + com.blazebit.persistence.testsuite.base.jpa.category.NoH2,com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate + + + + + org.apache.maven.surefire + surefire-junit47 + ${version.surefire.plugin} + + + + + + + + + post-10-unsafe-code + + [11,) + + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + \ No newline at end of file diff --git a/integration/jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs/EntityViewMessageBodyReader.java b/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/EntityViewMessageBodyReader.java similarity index 99% rename from integration/jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs/EntityViewMessageBodyReader.java rename to integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/EntityViewMessageBodyReader.java index 3de7c18c62..0cf692d04e 100644 --- a/integration/jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs/EntityViewMessageBodyReader.java +++ b/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/EntityViewMessageBodyReader.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs; +package com.blazebit.persistence.integration.jaxrs.jackson; import com.blazebit.persistence.CriteriaBuilder; import com.blazebit.persistence.FullQueryBuilder; import com.blazebit.persistence.integration.jackson.EntityViewAwareObjectMapper; import com.blazebit.persistence.integration.jackson.EntityViewIdValueAccessor; +import com.blazebit.persistence.integration.jaxrs.EntityViewId; import com.blazebit.persistence.view.ConvertOperationBuilder; import com.blazebit.persistence.view.ConvertOption; import com.blazebit.persistence.view.EntityViewBuilder; diff --git a/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/jersey/EntityViewIdAwareParameterServiceProvider.java b/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/jersey/EntityViewIdAwareParameterServiceProvider.java new file mode 100644 index 0000000000..5863ad3610 --- /dev/null +++ b/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/jersey/EntityViewIdAwareParameterServiceProvider.java @@ -0,0 +1,73 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jackson.jersey; + +import com.blazebit.persistence.integration.jaxrs.EntityViewId; +import org.glassfish.jersey.model.internal.spi.ParameterServiceProvider; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * @author Moritz Becker + * @since 1.5.0 + */ +public class EntityViewIdAwareParameterServiceProvider implements ParameterServiceProvider { + @Override + public Map getParameterAnnotationHelperMap() { + Map m = new WeakHashMap<>(); + m.put(EntityViewId.class, new org.glassfish.jersey.model.Parameter.ParamAnnotationHelper() { + public String getValueOf(EntityViewId a) { + return a.value().isEmpty() ? a.name() : a.value(); + } + + public org.glassfish.jersey.model.Parameter.Source getSource() { + return org.glassfish.jersey.model.Parameter.Source.ENTITY; + } + }); + return m; + } + + @Override + public Parameter.ParamCreationFactory getParameterCreationFactory() { + return new org.glassfish.jersey.model.Parameter.ParamCreationFactory() { + public boolean isFor(Class clazz) { + return clazz == org.glassfish.jersey.model.Parameter.class; + } + + public org.glassfish.jersey.model.Parameter createParameter(Annotation[] markers, Annotation marker, org.glassfish.jersey.model.Parameter.Source source, String sourceName, Class rawType, Type type, boolean encoded, String defaultValue) { + return new EntityViewIdAwareParameterServiceProvider.Parameter(markers, marker, source, sourceName, rawType, type, encoded, defaultValue); + } + + public org.glassfish.jersey.model.Parameter createBeanParameter(Annotation[] markers, Annotation marker, org.glassfish.jersey.model.Parameter.Source source, String sourceName, Class rawType, Type type, boolean encoded, String defaultValue) { + return this.createParameter(markers, marker, source, sourceName, rawType, type, encoded, defaultValue); + } + }; + } + + /** + * Custom parameter implementation required because construtor of org.glassfish.jersey.model.Parameter is protected. + */ + private static class Parameter extends org.glassfish.jersey.model.Parameter { + + Parameter(Annotation[] markers, Annotation marker, Source source, String sourceName, Class rawType, Type type, boolean encoded, String defaultValue) { + super(markers, marker, source, sourceName, rawType, type, encoded, defaultValue); + } + } +} diff --git a/integration/jaxrs/src/main/resources/META-INF/beans.xml b/integration/jaxrs-jackson/src/main/resources/META-INF/beans.xml similarity index 100% rename from integration/jaxrs/src/main/resources/META-INF/beans.xml rename to integration/jaxrs-jackson/src/main/resources/META-INF/beans.xml diff --git a/integration/jaxrs-jackson/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider b/integration/jaxrs-jackson/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider new file mode 100644 index 0000000000..e3e5101a7e --- /dev/null +++ b/integration/jaxrs-jackson/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider @@ -0,0 +1 @@ +com.blazebit.persistence.integration.jaxrs.jackson.jersey.EntityViewIdAwareParameterServiceProvider \ No newline at end of file diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/AbstractJaxrsTest.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/AbstractJaxrsTest.java similarity index 94% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/AbstractJaxrsTest.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/AbstractJaxrsTest.java index 6ec15a6dee..92b72115aa 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/AbstractJaxrsTest.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/AbstractJaxrsTest.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite; -import com.blazebit.persistence.integration.jaxrs.testsuite.config.EntityManagerFactoryHolder; -import com.blazebit.persistence.integration.jaxrs.testsuite.resource.Application; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.DocumentView; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.PersonView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.DocumentView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.PersonView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.config.EntityManagerFactoryHolder; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.resource.Application; import com.blazebit.persistence.view.EntityViewManager; import com.blazebit.persistence.view.IdMapping; import com.fasterxml.jackson.annotation.JsonAutoDetect; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/DocumentResourceTest.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/DocumentResourceTest.java similarity index 89% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/DocumentResourceTest.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/DocumentResourceTest.java index c0bbb3a08c..6cad1d714d 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/DocumentResourceTest.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/DocumentResourceTest.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Document; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Person; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.DocumentUpdateView; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.DocumentView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.DocumentUpdateView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.DocumentView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Document; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Person; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.Test; diff --git a/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/FromStringParamConverterProviderTest.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/FromStringParamConverterProviderTest.java new file mode 100644 index 0000000000..8b5cf6aceb --- /dev/null +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/FromStringParamConverterProviderTest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite; + +import com.blazebit.persistence.integration.jaxrs.jackson.EntityViewMessageBodyReader; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import static org.junit.Assert.assertEquals; + +/** + * @author Moritz Becker + * @since 1.5.0 + */ +public class FromStringParamConverterProviderTest { + + private static ParamConverterProvider FROM_STRING_PARAM_CONVERTER_PROVIDER; + + @BeforeClass + public static void prepare() throws IllegalAccessException, InstantiationException { + Class fromStringParamConverterProviderClass = null; + for (Class declaredClass : EntityViewMessageBodyReader.class.getDeclaredClasses()) { + if ("FromStringParamConverterProvider".equals(declaredClass.getSimpleName())) { + fromStringParamConverterProviderClass = (Class) declaredClass; + break; + } + } + try { + Constructor fromStringParamConverterProviderConstructor = fromStringParamConverterProviderClass.getDeclaredConstructor(); + fromStringParamConverterProviderConstructor.setAccessible(true); + FROM_STRING_PARAM_CONVERTER_PROVIDER = fromStringParamConverterProviderConstructor.newInstance(); + } catch (NoSuchMethodException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Test + public void convertInt() { + assertEquals(1, (int) convert(int.class, "1")); + } + + @Test + public void convertLong() { + assertEquals(1, (long) convert(long.class, "1")); + } + + @Test + public void convertFloat() { + assertEquals(1.01f, convert(float.class, "1.01"), 0.001); + } + + @Test + public void convertDouble() { + assertEquals(1.01, convert(double.class, "1.01"), 0.001); + } + + @Test + public void convertBoolean() { + assertEquals(true, convert(boolean.class, "true")); + } + + @Test + public void convertShort() { + assertEquals(1, (short) convert(short.class, "1")); + } + + @Test + public void convertByte() { + assertEquals(1, (byte) convert(byte.class, "1")); + } + + @Test + public void convertChar() { + assertEquals('a', (char) convert(char.class, "a")); + } + + private T convert(Class targetType, String value) { + ParamConverter converter = FROM_STRING_PARAM_CONVERTER_PROVIDER.getConverter(targetType, targetType, new Annotation[0]); + return converter == null ? null : converter.fromString(value); + } +} diff --git a/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/PersonResourceTest.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/PersonResourceTest.java new file mode 100644 index 0000000000..6373eca262 --- /dev/null +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/PersonResourceTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite; + +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.PersonCreateView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.PersonUpdateView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.PersonView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Person; +import org.junit.Test; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Moritz Becker + * @since 1.5.0 + */ +public class PersonResourceTest extends AbstractJaxrsTest { + + @Test + public void testUpdatePerson() throws Exception { + // Given + Person p1 = createPerson("P1"); + + // When + PersonUpdateView updateView = transactional(em -> { + return evm.find(em, PersonUpdateView.class, p1.getId()); + }); + updateView.setName("P2"); + PersonView updatedView = webTarget.path("/persons/{id}") + .resolveTemplate("id", p1.getId()) + .request() + .buildPut(Entity.entity(toJsonWithoutId(updateView), MediaType.APPLICATION_JSON_TYPE)) + .invoke(PersonViewImpl.class); + + // Then + assertEquals(updateView.getName(), updatedView.getName()); + } + + @Test + public void testCreatePerson() throws Exception { + // When + PersonCreateView personCreateView = evm.create(PersonCreateView.class); + personCreateView.setId(UUID.randomUUID().toString()); + personCreateView.setName("P1"); + Response response = webTarget.path("/persons") + .request() + .buildPost(Entity.entity(toJsonWithId(personCreateView), MediaType.APPLICATION_JSON_TYPE)) + .invoke(); + + // Then + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + PersonView createdPerson = transactional(em -> { + return evm.find(em, PersonView.class, personCreateView.getId()); + }); + assertNotNull(createdPerson); + } + + private Person createPerson(String name) { + return createPerson(name, 0L); + } + + private Person createPerson(String name, long age) { + return transactional(em -> { + Person p = new Person(name); + p.setAge(age); + em.persist(p); + return p; + }); + } +} diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/CriteriaBuilderFactoryProducer.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/CriteriaBuilderFactoryProducer.java similarity index 96% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/CriteriaBuilderFactoryProducer.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/CriteriaBuilderFactoryProducer.java index 94c94a4c88..48ca6aae83 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/CriteriaBuilderFactoryProducer.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/CriteriaBuilderFactoryProducer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.config; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.config; import com.blazebit.persistence.Criteria; import com.blazebit.persistence.CriteriaBuilderFactory; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityManagerFactoryHolder.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityManagerFactoryHolder.java similarity index 95% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityManagerFactoryHolder.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityManagerFactoryHolder.java index bfad3fb1b7..7259c28905 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityManagerFactoryHolder.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityManagerFactoryHolder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.config; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.config; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityManagerHolder.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityManagerHolder.java similarity index 94% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityManagerHolder.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityManagerHolder.java index e8c216749d..cce8bb5727 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityManagerHolder.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityManagerHolder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.config; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.config; import org.apache.deltaspike.jpa.api.transaction.TransactionScoped; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityViewManagerProducer.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityViewManagerProducer.java similarity index 97% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityViewManagerProducer.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityViewManagerProducer.java index 09496aaf9e..7906d456ae 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/config/EntityViewManagerProducer.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/config/EntityViewManagerProducer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.config; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.config; import com.blazebit.persistence.CriteriaBuilderFactory; import com.blazebit.persistence.view.EntityViewManager; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/entity/Document.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/entity/Document.java similarity index 96% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/entity/Document.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/entity/Document.java index e5c71f3e18..5b7177773f 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/entity/Document.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/entity/Document.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.entity; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity; import javax.persistence.Entity; import javax.persistence.FetchType; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/entity/Person.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/entity/Person.java similarity index 96% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/entity/Person.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/entity/Person.java index aeca7a8967..613303f8ef 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/entity/Person.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/entity/Person.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.entity; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity; import javax.persistence.Basic; import javax.persistence.Entity; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/Application.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/Application.java similarity index 92% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/Application.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/Application.java index dff0a67aee..b1a1e9e828 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/Application.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/Application.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.resource; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.resource; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import org.glassfish.jersey.server.ResourceConfig; @@ -31,7 +31,7 @@ public class Application extends ResourceConfig { public Application() { - this.packages("com.blazebit.persistence.integration.jaxrs"); + this.packages("com.blazebit.persistence.integration.jaxrs.jackson"); this.register(JacksonJsonProvider.class); } } diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/DocumentResource.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/DocumentResource.java similarity index 87% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/DocumentResource.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/DocumentResource.java index e28bbcb680..9474789bca 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/DocumentResource.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/DocumentResource.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.resource; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.resource; import com.blazebit.persistence.integration.jaxrs.EntityViewId; -import com.blazebit.persistence.integration.jaxrs.testsuite.config.EntityManagerHolder; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.DocumentUpdateView; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.DocumentView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.config.EntityManagerHolder; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.DocumentUpdateView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.DocumentView; import com.blazebit.persistence.view.EntityViewManager; import org.apache.deltaspike.jpa.api.transaction.Transactional; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/PersonResource.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/PersonResource.java similarity index 82% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/PersonResource.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/PersonResource.java index 5d6fc0f3c3..72d3b87027 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/resource/PersonResource.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/resource/PersonResource.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.resource; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.resource; import com.blazebit.persistence.integration.jaxrs.EntityViewId; -import com.blazebit.persistence.integration.jaxrs.testsuite.config.EntityManagerHolder; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.PersonCreateView; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.PersonUpdateView; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.PersonView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.config.EntityManagerHolder; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.PersonCreateView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.PersonUpdateView; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view.PersonView; import com.blazebit.persistence.view.EntityViewManager; import org.apache.deltaspike.jpa.api.transaction.Transactional; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/DocumentUpdateView.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/DocumentUpdateView.java similarity index 86% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/DocumentUpdateView.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/DocumentUpdateView.java index b4b072814a..260d2e540d 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/DocumentUpdateView.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/DocumentUpdateView.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.view; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Document; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Document; import com.blazebit.persistence.view.EntityView; import com.blazebit.persistence.view.IdMapping; import com.blazebit.persistence.view.UpdatableEntityView; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/DocumentView.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/DocumentView.java similarity index 87% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/DocumentView.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/DocumentView.java index 52e0aae1db..d9405679fb 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/DocumentView.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/DocumentView.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.view; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Document; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Document; import com.blazebit.persistence.view.EntityView; import com.blazebit.persistence.view.IdMapping; import com.blazebit.persistence.view.Mapping; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonCreateView.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonCreateView.java similarity index 86% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonCreateView.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonCreateView.java index 38b35310ff..ee4c757046 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonCreateView.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonCreateView.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.view; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Person; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Person; import com.blazebit.persistence.view.CreatableEntityView; import com.blazebit.persistence.view.EntityView; import com.blazebit.persistence.view.IdMapping; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonUpdateView.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonUpdateView.java similarity index 86% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonUpdateView.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonUpdateView.java index 0bf251f01c..1a1344e170 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonUpdateView.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonUpdateView.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.view; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Person; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Person; import com.blazebit.persistence.view.EntityView; import com.blazebit.persistence.view.IdMapping; import com.blazebit.persistence.view.UpdatableEntityView; diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonView.java b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonView.java similarity index 84% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonView.java rename to integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonView.java index e3cda3c70e..d56682baec 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/view/PersonView.java +++ b/integration/jaxrs-jackson/src/test/java/com/blazebit/persistence/integration/jaxrs/jackson/testsuite/view/PersonView.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite.view; +package com.blazebit.persistence.integration.jaxrs.jackson.testsuite.view; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Person; +import com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Person; import com.blazebit.persistence.view.EntityView; import com.blazebit.persistence.view.IdMapping; diff --git a/integration/jaxrs/src/test/resources/META-INF/beans.xml b/integration/jaxrs-jackson/src/test/resources/META-INF/beans.xml similarity index 100% rename from integration/jaxrs/src/test/resources/META-INF/beans.xml rename to integration/jaxrs-jackson/src/test/resources/META-INF/beans.xml diff --git a/integration/jaxrs-jackson/src/test/resources/META-INF/persistence.xml b/integration/jaxrs-jackson/src/test/resources/META-INF/persistence.xml new file mode 100644 index 0000000000..e82b460925 --- /dev/null +++ b/integration/jaxrs-jackson/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,31 @@ + + + + + com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Document + com.blazebit.persistence.integration.jaxrs.jackson.testsuite.entity.Person + true + + + + + + + + + diff --git a/integration/jaxrs/src/test/resources/logging.properties b/integration/jaxrs-jackson/src/test/resources/logging.properties similarity index 100% rename from integration/jaxrs/src/test/resources/logging.properties rename to integration/jaxrs-jackson/src/test/resources/logging.properties diff --git a/integration/jaxrs-jsonb-jakarta/pom.xml b/integration/jaxrs-jsonb-jakarta/pom.xml new file mode 100644 index 0000000000..361d49061f --- /dev/null +++ b/integration/jaxrs-jsonb-jakarta/pom.xml @@ -0,0 +1,171 @@ + + + + + 4.0.0 + + + com.blazebit + blaze-persistence-integration + 1.6.4-SNAPSHOT + ../pom.xml + + + blaze-persistence-integration-jaxrs-jsonb-jakarta + jar + + + + ${project.groupId} + blaze-persistence-integration-jaxrs-jsonb + provided + + + jakarta.persistence + jakarta.persistence-api + ${version.jakarta-jpa-api} + + + ${project.groupId} + blaze-persistence-entity-view-api-jakarta + + + ${project.groupId} + blaze-persistence-integration-jsonb-jakarta + + + ${project.groupId} + blaze-common-utils + + + + + + + maven-antrun-plugin + + + transform-jar + package + + run + + + + + + + + + + + + + + + + + + + + + + transform-sources-jar + package + + run + + + + + + + + + + + + + + + + + + + + + + + + transform-javadoc + package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + org.eclipse.transformer + org.eclipse.transformer.cli + 0.2.0 + + + ant-contrib + ant-contrib + 1.0b3 + + + ant + ant + + + + + + + + + diff --git a/integration/jaxrs-jsonb/pom.xml b/integration/jaxrs-jsonb/pom.xml new file mode 100644 index 0000000000..4a8b8d5283 --- /dev/null +++ b/integration/jaxrs-jsonb/pom.xml @@ -0,0 +1,258 @@ + + + + blaze-persistence-integration + com.blazebit + 1.6.4-SNAPSHOT + ../pom.xml + + 4.0.0 + + blaze-persistence-integration-jaxrs-jsonb + + Blazebit Persistence Integration JAX-RS JSONB + + + com.blazebit.persistence.integration.jaxrs.jsonb + + UTF-8 + 1.8 + 1.8 + + 1.9.3 + 5.4.6.Final + 5.4 + 2.30.1 + 2.1.1 + + + + + + org.apache.deltaspike.distribution + distributions-bom + ${version.deltaspike} + pom + import + + + + + + + ${project.groupId} + blaze-persistence-entity-view-api + + + jakarta.json.bind + jakarta.json.bind-api + 1.0.2 + provided + + + jakarta.json + jakarta.json-api + 1.1.6 + provided + + + ${project.groupId} + blaze-persistence-integration-jsonb + + + ${project.groupId} + blaze-persistence-integration-jaxrs + + + ${project.groupId} + blaze-common-utils + + + javax.ws.rs + javax.ws.rs-api + ${version.jaxrs} + provided + + + org.glassfish.jersey.core + jersey-common + ${version.jersey} + true + + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.0.Final + provided + + + javax.enterprise + cdi-api + 2.0.SP1 + provided + + + jakarta.annotation + jakarta.annotation-api + ${version.annotation} + provided + + + org.eclipse + yasson + 1.0.9 + test + + + com.blazebit + blaze-persistence-integration-entity-view-cdi + test + + + com.blazebit + blaze-persistence-core-impl + test + + + com.blazebit + blaze-persistence-entity-view-impl + test + + + ${project.groupId} + blaze-persistence-testsuite-base-jpa + test + + + com.blazebit + blaze-persistence-integration-hibernate-${version.integration-hibernate} + test + + + org.hibernate + hibernate-entitymanager + ${version.hibernate} + test + + + org.apache.deltaspike.modules + deltaspike-jpa-module-impl + test + + + org.glassfish.jersey.core + jersey-server + ${version.jersey} + test + + + org.glassfish.jersey.containers + jersey-container-jetty-http + ${version.jersey} + test + + + org.glassfish.jersey.inject + jersey-cdi2-se + ${version.jersey} + test + + + org.jboss + jandex + + + javax.enterprise + cdi-api + + + + + com.h2database + h2 + test + + + junit + junit + test + + + + + jakarta.xml.bind + jakarta.xml.bind-api + ${version.jaxb-api} + test + + + com.sun.xml.bind + jaxb-impl + ${version.jaxb} + test + + + + + + + org.moditect + moditect-maven-plugin + + + add-module-infos + package + + add-module-info + + + + + module ${module.name} { + requires transitive com.blazebit.persistence.view; + requires transitive com.blazebit.persistence.integration.jaxrs; + requires com.blazebit.common.utils; + requires com.blazebit.persistence.integration.jsonb; + exports com.blazebit.persistence.integration.jaxrs.jsonb; + provides org.glassfish.jersey.model.internal.spi.ParameterServiceProvider with com.blazebit.persistence.integration.jaxrs.jsonb.jersey.EntityViewIdAwareParameterServiceProvider; + } + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + com.blazebit.persistence.testsuite.base.jpa.category.NoH2,com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate + + + + + org.apache.maven.surefire + surefire-junit47 + ${version.surefire.plugin} + + + + + + + + + post-10-unsafe-code + + [11,) + + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + \ No newline at end of file diff --git a/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java b/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java new file mode 100644 index 0000000000..ff855d1f66 --- /dev/null +++ b/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java @@ -0,0 +1,543 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb; + +import com.blazebit.persistence.CriteriaBuilder; +import com.blazebit.persistence.FullQueryBuilder; +import com.blazebit.persistence.integration.jaxrs.EntityViewId; +import com.blazebit.persistence.integration.jsonb.EntityViewIdValueAccessor; +import com.blazebit.persistence.integration.jsonb.EntityViewJsonbDeserializer; +import com.blazebit.persistence.view.ConvertOperationBuilder; +import com.blazebit.persistence.view.ConvertOption; +import com.blazebit.persistence.view.EntityViewBuilder; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import com.blazebit.persistence.view.FlushOperationBuilder; +import com.blazebit.persistence.view.change.SingularChangeModel; +import com.blazebit.persistence.view.metamodel.ViewMetamodel; + +import javax.annotation.PostConstruct; +import javax.annotation.Priority; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbConfig; +import javax.json.bind.serializer.DeserializationContext; +import javax.json.stream.JsonParser; +import javax.persistence.EntityManager; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.Consumes; +import javax.ws.rs.Priorities; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; +import javax.ws.rs.ext.Provider; +import javax.ws.rs.ext.Providers; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Christian Beikov + * @since 1.4.0 + */ +@Priority(Priorities.USER - 1) +@Provider +// "*/*" needs to be included since Jersey does not support the "application/*+json" notation +@Consumes({"application/json", "application/*+json", "text/json", "*/*"}) +public class EntityViewMessageBodyReader implements MessageBodyReader { + + private static final ParamConverterProvider FROM_STRING_PARAM_CONVERTER_PROVIDER = new FromStringParamConverterProvider(); + + @Inject + private Instance entityViewManager; + @Inject + private Instance jsonbConfig; + @Inject + @Any + private Instance paramConverterProviders; + @Context + private UriInfo uriInfo; + @Context + private Providers providers; + + private Jsonb jsonb; + private final ThreadLocal idValueHolder = new ThreadLocal<>(); + + @PostConstruct + public void init() { + if (entityViewManager.isUnsatisfied()) { + this.jsonb = null; + } else { + JsonbConfig config = null; + ContextResolver resolver; + if (providers != null && (resolver = providers.getContextResolver(JsonbConfig.class, MediaType.APPLICATION_JSON_TYPE)) != null) { + config = resolver.getContext(EntityViewMessageBodyReader.class); + } + if (config == null) { + if (jsonbConfig.isUnsatisfied()) { + config = new JsonbConfig(); + } else { + config = jsonbConfig.get(); + } + } + EntityViewJsonbDeserializer.integrate(config, entityViewManager.get(), new EntityViewIdValueAccessor() { + @Override + public T getValue(JsonParser jsonParser, DeserializationContext deserializationContext, Class idType) { + String value = idValueHolder.get(); + if (value == null || String.class.equals(idType)) { + return (T) value; + } else { + ParamConverter paramConverter = null; + for (ParamConverterProvider paramConverterProvider : paramConverterProviders) { + if ((paramConverter = paramConverterProvider.getConverter(idType, idType, null)) != null) { + break; + } + } + if (paramConverter == null) { + paramConverter = FROM_STRING_PARAM_CONVERTER_PROVIDER.getConverter(idType, idType, null); + } + if (paramConverter == null) { + throw new RuntimeException("No " + ParamConverter.class.getName() + " could be found to convert to type " + idType.getName()); + } else { + return paramConverter.fromString(value); + } + } + } + }); + this.jsonb = JsonbBuilder.create(config); +// this.jsonb = new EntityViewAwareObjectMapper(new LazyEntityViewManager(), config, ); + } + } + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return !entityViewManager.isUnsatisfied() + && entityViewManager.get().getMetamodel().view(type) != null + && hasMatchingMediaType(mediaType) + && !InputStream.class.isAssignableFrom(type) + && !Reader.class.isAssignableFrom(type); + } + + @Override + public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + EntityViewId entityViewAnnotation = null; + for (Annotation annotation : annotations) { + if (annotation.annotationType().equals(EntityViewId.class)) { + entityViewAnnotation = (EntityViewId) annotation; + break; + } + } + if (entityViewAnnotation != null) { + String pathVariableName = entityViewAnnotation.value().isEmpty() ? entityViewAnnotation.name() : entityViewAnnotation.value(); + if (pathVariableName.isEmpty()) { + throw new IllegalArgumentException( + "Entity view id path param name for argument type [" + type.getName() + + "] not available."); + } + String pathVariableStringValue = uriInfo.getPathParameters().getFirst(pathVariableName); + idValueHolder.set(pathVariableStringValue); + } + + try { + if (jsonb != null) { + return jsonb.fromJson(entityStream, genericType); + } + } finally { + idValueHolder.remove(); + } + + return null; + } + + /** + * Copy of {@link com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider#hasMatchingMediaType(javax.ws.rs.core.MediaType)} + * + * @param mediaType the media type to be matched + * @return true, if this reader accepts the given mediaType or false otherwise + */ + private boolean hasMatchingMediaType(MediaType mediaType) { + /* As suggested by Stephen D, there are 2 ways to check: either + * being as inclusive as possible (if subtype is "json"), or + * exclusive (major type "application", minor type "json"). + * Let's start with inclusive one, hard to know which major + * types we should cover aside from "application". + */ + if (mediaType != null) { + // Ok: there are also "xxx+json" subtypes, which count as well + String subtype = mediaType.getSubtype(); + + // [Issue#14]: also allow 'application/javascript' + return "json".equalsIgnoreCase(subtype) + || subtype.endsWith("+json") + || "javascript".equals(subtype) + // apparently Microsoft once again has interesting alternative types? + || "x-javascript".equals(subtype) + || "x-json".equals(subtype) // [Issue#40] + ; + } + /* Not sure if this can happen; but it seems reasonable + * that we can at least produce JSON without media type? + */ + return true; + } + + /** + * ParamConverterProvider for default parameter conversion using fromString method if present + */ + private static class FromStringParamConverterProvider implements ParamConverterProvider { + + private static final Map, ParamConverter> CACHED_PARAM_CONVERTERS = new ConcurrentHashMap<>(); + + @Override + public ParamConverter getConverter(Class clazz, Type type, Annotation[] annotations) { + ParamConverter converter = (ParamConverter) CACHED_PARAM_CONVERTERS.get(clazz); + if (converter == null) { + // Follow the logic described in https://docs.jboss.org/resteasy/docs/3.5.0.Final/userguide/html/StringConverter.html#d4e1480 + Class effectiveClass; + if (short.class.equals(clazz)) { + effectiveClass = Short.class; + } else if (int.class.equals(clazz)) { + effectiveClass = Integer.class; + } else if (long.class.equals(clazz)) { + effectiveClass = Long.class; + } else if (float.class.equals(clazz)) { + effectiveClass = Float.class; + } else if (double.class.equals(clazz)) { + effectiveClass = Double.class; + } else if (boolean.class.equals(clazz)) { + effectiveClass = Boolean.class; + } else if (byte.class.equals(clazz)) { + effectiveClass = Byte.class; + } else if (char.class.equals(clazz)) { + effectiveClass = Character.class; + } else { + effectiveClass = clazz; + } + Method fromStringMethod = null; + try { + fromStringMethod = effectiveClass.getMethod("fromString", String.class); + } catch (NoSuchMethodException e) { + // ignore + } + Method valueOfMethod = null; + try { + valueOfMethod = effectiveClass.getMethod("valueOf", String.class); + } catch (NoSuchMethodException e) { + // ignore + } + + Method effectiveMethod; + if (fromStringMethod != null && valueOfMethod != null) { + effectiveMethod = effectiveClass.isEnum() ? fromStringMethod : valueOfMethod; + } else if (fromStringMethod != null) { + effectiveMethod = fromStringMethod; + } else { + effectiveMethod = valueOfMethod; + } + + if (effectiveClass == Character.class) { + converter = (ParamConverter) new CharacterParamConverter(); + } else if (effectiveMethod == null) { + Constructor constructor = null; + try { + constructor = ((Class) effectiveClass).getConstructor(String.class); + } catch (NoSuchMethodException e) { + // ignore + } + converter = constructor == null ? null : new ConstructorBasedParamConverter<>(constructor); + } else { + converter = new MethodBasedParamConverter<>(effectiveMethod); + } + if (converter != null) { + CACHED_PARAM_CONVERTERS.put(clazz, converter); + } + } + return converter; + } + + /** + * ParamConverter for default parameter conversion using the given {@link java.lang.String} consuming method + * + * @param The parameter type + */ + private static class MethodBasedParamConverter implements javax.ws.rs.ext.ParamConverter { + private final Method stringConsumingMethod; + + public MethodBasedParamConverter(Method stringConsumingMethod) { + this.stringConsumingMethod = stringConsumingMethod; + } + + @Override + @SuppressWarnings("unchecked") + public T fromString(String s) { + try { + return (T) stringConsumingMethod.invoke(null, s); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new BadRequestException("Malformed input: " + s); + } + } + + @Override + public String toString(T o) { + return o.toString(); + } + } + + /** + * ParamConverter for default parameter conversion using the given {@link java.lang.String} consuming constructor + * + * @param The parameter type + */ + private static class ConstructorBasedParamConverter implements javax.ws.rs.ext.ParamConverter { + private final Constructor stringConstructor; + + public ConstructorBasedParamConverter(Constructor stringConstructor) { + this.stringConstructor = stringConstructor; + } + + @Override + @SuppressWarnings("unchecked") + public T fromString(String s) { + try { + return (T) stringConstructor.newInstance(s); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new BadRequestException("Malformed input: " + s); + } + } + + @Override + public String toString(T o) { + return o.toString(); + } + } + + /** + * ParamConverter for default parameter conversion for {@link java.lang.Character} + */ + private static class CharacterParamConverter implements javax.ws.rs.ext.ParamConverter { + + @Override + @SuppressWarnings("unchecked") + public Character fromString(String s) { + Character c; + if (s == null || s.length() > 1) { + c = null; + } else { + c = s.charAt(0); + } + return c; + } + + @Override + public String toString(Character o) { + return o.toString(); + } + } + } + + /** + * @author Moritz Becker + * @since 1.6.0 + */ + private final class LazyEntityViewManager implements EntityViewManager { + public ViewMetamodel getMetamodel() { + return entityViewManager.get().getMetamodel(); + } + + public Map getOptionalParameters() { + return entityViewManager.get().getOptionalParameters(); + } + + public T find(EntityManager entityManager, Class entityViewClass, Object entityId) { + return entityViewManager.get().find(entityManager, entityViewClass, entityId); + } + + public T find(EntityManager entityManager, EntityViewSetting> entityViewSetting, Object entityId) { + return entityViewManager.get().find(entityManager, entityViewSetting, entityId); + } + + public T getReference(Class entityViewClass, Object id) { + return entityViewManager.get().getReference(entityViewClass, id); + } + + public T getEntityReference(EntityManager entityManager, Object entityView) { + return entityViewManager.get().getEntityReference(entityManager, entityView); + } + + public SingularChangeModel getChangeModel(T entityView) { + return entityViewManager.get().getChangeModel(entityView); + } + + public T create(Class entityViewClass) { + return entityViewManager.get().create(entityViewClass); + } + + public T create(Class entityViewClass, Map optionalParameters) { + return entityViewManager.get().create(entityViewClass, optionalParameters); + } + + public EntityViewBuilder createBuilder(Class clazz) { + return entityViewManager.get().createBuilder(clazz); + } + + public EntityViewBuilder createBuilder(Class clazz, String constructorName) { + return entityViewManager.get().createBuilder(clazz, constructorName); + } + + public EntityViewBuilder createBuilder(X view) { + return entityViewManager.get().createBuilder(view); + } + + public EntityViewBuilder createBuilder(X view, String constructorName) { + return entityViewManager.get().createBuilder(view, constructorName); + } + + public EntityViewBuilder createBuilder(Class clazz, Map optionalParameters) { + return entityViewManager.get().createBuilder(clazz, optionalParameters); + } + + public EntityViewBuilder createBuilder(Class clazz, Map optionalParameters, String constructorName) { + return entityViewManager.get().createBuilder(clazz, optionalParameters, constructorName); + } + + public EntityViewBuilder createBuilder(X view, Map optionalParameters) { + return entityViewManager.get().createBuilder(view, optionalParameters); + } + + public EntityViewBuilder createBuilder(X view, Map optionalParameters, String constructorName) { + return entityViewManager.get().createBuilder(view, optionalParameters, constructorName); + } + + public T convert(Object source, Class entityViewClass, ConvertOption... convertOptions) { + return entityViewManager.get().convert(source, entityViewClass, convertOptions); + } + + public T convert(Object source, Class entityViewClass, String constructorName, ConvertOption... convertOptions) { + return entityViewManager.get().convert(source, entityViewClass, constructorName, convertOptions); + } + + public T convert(Object source, Class entityViewClass, Map optionalParameters, ConvertOption... convertOptions) { + return entityViewManager.get().convert(source, entityViewClass, optionalParameters, convertOptions); + } + + public T convert(Object source, Class entityViewClass, String constructorName, Map optionalParameters, ConvertOption... convertOptions) { + return entityViewManager.get().convert(source, entityViewClass, constructorName, optionalParameters, convertOptions); + } + + public ConvertOperationBuilder convertWith(Object source, Class entityViewClass, ConvertOption... convertOptions) { + return entityViewManager.get().convertWith(source, entityViewClass, convertOptions); + } + + public ConvertOperationBuilder convertWith(Object source, Class entityViewClass, String constructorName, ConvertOption... convertOptions) { + return entityViewManager.get().convertWith(source, entityViewClass, constructorName, convertOptions); + } + + public ConvertOperationBuilder convertWith(Object source, Class entityViewClass, Map optionalParameters, ConvertOption... convertOptions) { + return entityViewManager.get().convertWith(source, entityViewClass, optionalParameters, convertOptions); + } + + public ConvertOperationBuilder convertWith(Object source, Class entityViewClass, String constructorName, Map optionalParameters, ConvertOption... convertOptions) { + return entityViewManager.get().convertWith(source, entityViewClass, constructorName, optionalParameters, convertOptions); + } + + public void save(EntityManager entityManager, Object view) { + entityViewManager.get().save(entityManager, view); + } + + public void saveFull(EntityManager entityManager, Object view) { + entityViewManager.get().saveFull(entityManager, view); + } + + public void saveTo(EntityManager entityManager, Object view, Object entity) { + entityViewManager.get().saveTo(entityManager, view, entity); + } + + public void saveFullTo(EntityManager entityManager, Object view, Object entity) { + entityViewManager.get().saveFullTo(entityManager, view, entity); + } + + @Deprecated + public void update(EntityManager entityManager, Object view) { + entityViewManager.get().update(entityManager, view); + } + + @Deprecated + public void updateFull(EntityManager entityManager, Object view) { + entityViewManager.get().updateFull(entityManager, view); + } + + public FlushOperationBuilder saveWith(EntityManager entityManager, Object view) { + return entityViewManager.get().saveWith(entityManager, view); + } + + public FlushOperationBuilder saveFullWith(EntityManager entityManager, Object view) { + return entityViewManager.get().saveFullWith(entityManager, view); + } + + public FlushOperationBuilder saveWithTo(EntityManager entityManager, Object view, Object entity) { + return entityViewManager.get().saveWithTo(entityManager, view, entity); + } + + public FlushOperationBuilder saveFullWithTo(EntityManager entityManager, Object view, Object entity) { + return entityViewManager.get().saveFullWithTo(entityManager, view, entity); + } + + public void remove(EntityManager entityManager, Object view) { + entityViewManager.get().remove(entityManager, view); + } + + public FlushOperationBuilder removeWith(EntityManager entityManager, Object view) { + return entityViewManager.get().removeWith(entityManager, view); + } + + public void remove(EntityManager entityManager, Class entityViewClass, Object viewId) { + entityViewManager.get().remove(entityManager, entityViewClass, viewId); + } + + public FlushOperationBuilder removeWith(EntityManager entityManager, Class entityViewClass, Object viewId) { + return entityViewManager.get().removeWith(entityManager, entityViewClass, viewId); + } + + public > Q applySetting(EntityViewSetting setting, CriteriaBuilder criteriaBuilder) { + return entityViewManager.get().applySetting(setting, criteriaBuilder); + } + + public > Q applySetting(EntityViewSetting setting, CriteriaBuilder criteriaBuilder, String entityViewRoot) { + return entityViewManager.get().applySetting(setting, criteriaBuilder, entityViewRoot); + } + + public T getService(Class serviceClass) { + return entityViewManager.get().getService(serviceClass); + } + } +} diff --git a/integration/jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs/jersey/EntityViewIdAwareParameterServiceProvider.java b/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/jersey/EntityViewIdAwareParameterServiceProvider.java similarity index 98% rename from integration/jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs/jersey/EntityViewIdAwareParameterServiceProvider.java rename to integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/jersey/EntityViewIdAwareParameterServiceProvider.java index ec299d0a6e..91517e1883 100644 --- a/integration/jaxrs/src/main/java/com/blazebit/persistence/integration/jaxrs/jersey/EntityViewIdAwareParameterServiceProvider.java +++ b/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/jersey/EntityViewIdAwareParameterServiceProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.jersey; +package com.blazebit.persistence.integration.jaxrs.jsonb.jersey; import com.blazebit.persistence.integration.jaxrs.EntityViewId; import org.glassfish.jersey.model.internal.spi.ParameterServiceProvider; diff --git a/integration/jaxrs-jsonb/src/main/resources/META-INF/beans.xml b/integration/jaxrs-jsonb/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000000..8494c16f89 --- /dev/null +++ b/integration/jaxrs-jsonb/src/main/resources/META-INF/beans.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/integration/jaxrs-jsonb/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider b/integration/jaxrs-jsonb/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider new file mode 100644 index 0000000000..9c0a1362c7 --- /dev/null +++ b/integration/jaxrs-jsonb/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider @@ -0,0 +1 @@ +com.blazebit.persistence.integration.jaxrs.jsonb.jersey.EntityViewIdAwareParameterServiceProvider \ No newline at end of file diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/AbstractJaxrsTest.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/AbstractJaxrsTest.java new file mode 100644 index 0000000000..21d07e10e9 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/AbstractJaxrsTest.java @@ -0,0 +1,285 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.config.EntityManagerFactoryHolder; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.resource.Application; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.DocumentView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.PersonView; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.spi.type.EntityViewProxy; +import org.eclipse.jetty.server.Server; +import org.glassfish.jersey.jetty.JettyHttpContainer; +import org.glassfish.jersey.jetty.JettyHttpContainerFactory; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +import javax.inject.Inject; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbConfig; +import javax.json.bind.config.PropertyVisibilityStrategy; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import javax.persistence.Persistence; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.UriBuilder; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.BindException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +/** + * @author Christian Beikov + * @since 1.5.0 + */ +public abstract class AbstractJaxrsTest { + + private static final Logger LOG = Logger.getLogger(AbstractJaxrsTest.class.getName()); + private static final int SERVER_START_PORT = 18080; + private static final String SERVER_HOST = "localhost"; + private static Server SERVER; + + @Inject + private EntityManagerFactoryHolder emfHolder; + private EntityManager em; + @Inject + protected EntityViewManager evm; + protected WebTarget webTarget; + + private final Jsonb jsonb = JsonbBuilder.create(); + + @BeforeClass + public static void initLogging() { + try { + LogManager.getLogManager().readConfiguration(AbstractJaxrsTest.class.getResourceAsStream( + "/logging.properties")); + } catch (Exception e) { + e.printStackTrace(System.err); + } + } + + @BeforeClass + public static void bootContainer() throws Exception { + int port = SERVER_START_PORT; + boolean created = false; + while (!created) { + URI baseUri = UriBuilder.fromUri("http://" + SERVER_HOST).port(port++).build(); + try { + SERVER = JettyHttpContainerFactory.createServer(baseUri, new Application()); + created = true; + } catch (ProcessingException e) { + if (!(getRootCause(e) instanceof BindException)) { + throw e; + } + LOG.log(Level.SEVERE, "Can't create http endpoint. Retrying with different port...", e); + } + } + SERVER.start(); + while (!SERVER.isStarted()) { + LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10)); + } + } + + private static Throwable getRootCause(Throwable t) { + while (t.getCause() != null) { + t = t.getCause(); + } + return t; + } + + @AfterClass + public static void shutdownContainer() throws Exception { + SERVER.stop(); + } + + @Before + public void initInstance() { + ((JettyHttpContainer) SERVER.getHandler()).getApplicationHandler().getInjectionManager().inject(this); + dropAndCreateSchema(); + this.webTarget = ClientBuilder.newClient() + .register(JsonbJsonProvider.class) + .target(SERVER.getURI()); + this.em = emfHolder.getEntityManagerFactory().createEntityManager(); + } + + private void dropAndCreateSchema() { + Map properties = new HashMap<>(); + properties.put("javax.persistence.schema-generation.database.action", "drop-and-create"); + Persistence.createEntityManagerFactory("TestsuiteBase", properties); + } + + @After + public void closeEntityManager() { + em.close(); + } + + protected void transactional(Consumer consumer) { + transactional(em -> { + consumer.accept(em); + return null; + }); + } + + protected T transactional(Function producer) { + EntityTransaction tx = em.getTransaction(); + boolean success = false; + + T result; + try { + tx.begin(); + result = producer.apply(em); + success = true; + } finally { + if (success) { + tx.commit(); + } else { + tx.rollback(); + } + } + return result; + } + + protected byte[] toJsonWithId(Object entityView) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + jsonb.toJson(entityView, os); + return os.toByteArray(); + } + + protected byte[] toJsonWithoutId(Object entityView) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + JsonbConfig config = new JsonbConfig() + .withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { + @Override + public boolean isVisible(Field field) { + return false; + } + + @Override + public boolean isVisible(Method m) { + if (EntityViewProxy.class.isAssignableFrom(m.getDeclaringClass())) { + Class superclass = m.getDeclaringClass().getSuperclass(); + if (superclass == Object.class) { + superclass = m.getDeclaringClass().getInterfaces()[0]; + } + try { + m = superclass.getMethod(m.getName(), m.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + return m.getAnnotation(IdMapping.class) == null && Modifier.isPublic(m.getModifiers()); + } + }); + JsonbBuilder.create(config).toJson(entityView, os); + return os.toByteArray(); + } + + protected static final class DocumentViewImpl implements DocumentView { + + private Long id; + private String name; + private PersonViewImpl owner; + private long ownerDocumentCount; + private String optionalParameter; + + @Override + public Long getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + @Override + public PersonViewImpl getOwner() { + return owner; + } + + @Override + public long getOwnerDocumentCount() { + return ownerDocumentCount; + } + + @Override + public String getOptionalParameter() { + return optionalParameter; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setOwner(PersonViewImpl owner) { + this.owner = owner; + } + + public void setOwnerDocumentCount(long ownerDocumentCount) { + this.ownerDocumentCount = ownerDocumentCount; + } + + public void setOptionalParameter(String optionalParameter) { + this.optionalParameter = optionalParameter; + } + } + + protected static final class PersonViewImpl implements PersonView { + private UUID id; + private String name; + + @Override + public UUID getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + public void setId(UUID id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/DocumentResourceTest.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/DocumentResourceTest.java new file mode 100644 index 0000000000..732a8003cc --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/DocumentResourceTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Document; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Person; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.DocumentUpdateView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.DocumentView; +import org.junit.Test; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.MediaType; + +import static org.junit.Assert.assertEquals; + +/** + * @author Christian Beikov + * @since 1.5.0 + */ +public class DocumentResourceTest extends AbstractJaxrsTest { + + @Test + public void testUpdateDocument1() { + // Given + Document d1 = createDocument("D1"); + + // When + DocumentUpdateView updateView = transactional(em -> { + return evm.find(em, DocumentUpdateView.class, d1.getId()); + }); + updateView.setName("D2"); + DocumentView updatedView = webTarget.path("/documents/{id}") + .resolveTemplate("id", d1.getId()) + .request() + .buildPut(Entity.entity(toJsonWithoutId(updateView), "application/vnd.blazebit.update1+json")) + .invoke(DocumentViewImpl.class); + + // Then + assertEquals(updateView.getName(), updatedView.getName()); + } + + @Test + public void testUpdateDocument2() { + // Given + Document d1 = createDocument("D1"); + + // When + DocumentUpdateView updateView = transactional(em -> { + return evm.find(em, DocumentUpdateView.class, d1.getId()); + }); + updateView.setName("D2"); + DocumentView updatedView = webTarget.path("/documents/{id}") + .resolveTemplate("id", d1.getId()) + .request() + .buildPut(Entity.entity(toJsonWithoutId(updateView), "application/vnd.blazebit.update2+json")) + .invoke(DocumentViewImpl.class); + + // Then + assertEquals(updateView.getName(), updatedView.getName()); + } + + @Test + public void testUpdateDocument3() { + // Given + Document d1 = createDocument("D1"); + + // When + DocumentUpdateView updateView = transactional(em -> { + return evm.find(em, DocumentUpdateView.class, d1.getId()); + }); + updateView.setName("D2"); + DocumentView updatedView = webTarget.path("/documents") + .request() + .buildPut(Entity.entity(toJsonWithId(updateView), MediaType.APPLICATION_JSON_TYPE)) + .invoke(DocumentViewImpl.class); + + // Then + assertEquals(updateView.getName(), updatedView.getName()); + } + + private Document createDocument(String name) { + return createDocument(name, null); + } + + private Document createDocument(final String name, final Person owner) { + return createDocument(name, null, 0L, owner); + } + + private Document createDocument(final String name, final String description, final long age, final Person owner) { + return transactional(em -> { + Document d = new Document(name); + d.setDescription(description); + d.setAge(age); + d.setOwner(owner); + em.persist(d); + return d; + }); + } +} diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/FromStringParamConverterProviderTest.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/FromStringParamConverterProviderTest.java similarity index 95% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/FromStringParamConverterProviderTest.java rename to integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/FromStringParamConverterProviderTest.java index b475595da7..3f27431e71 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/FromStringParamConverterProviderTest.java +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/FromStringParamConverterProviderTest.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite; +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite; -import com.blazebit.persistence.integration.jaxrs.EntityViewMessageBodyReader; +import com.blazebit.persistence.integration.jaxrs.jsonb.EntityViewMessageBodyReader; import org.junit.BeforeClass; import org.junit.Test; diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/JsonbJsonProvider.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/JsonbJsonProvider.java new file mode 100644 index 0000000000..7d043fc7bc --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/JsonbJsonProvider.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite; + +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Provider +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class JsonbJsonProvider implements MessageBodyWriter, MessageBodyReader { + + private final Jsonb jsonb = JsonbBuilder.create(); + + @Override + public boolean isReadable(final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { + return !InputStream.class.isAssignableFrom(type) + && !Reader.class.isAssignableFrom(type) + && !Response.class.isAssignableFrom(type) + && !CharSequence.class.isAssignableFrom(type); + } + + @Override + public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { + return !InputStream.class.isAssignableFrom(type) + && !OutputStream.class.isAssignableFrom(type) + && !Writer.class.isAssignableFrom(type) + && !StreamingOutput.class.isAssignableFrom(type) + && !CharSequence.class.isAssignableFrom(type) + && !Response.class.isAssignableFrom(type); + } + + @Override + public long getSize(T t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return -1; + } + + @Override + public T readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + return (T) jsonb.fromJson(entityStream, genericType); + } + + @Override + public void writeTo(T t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { + jsonb.toJson(t, genericType, entityStream); + } +} diff --git a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/PersonResourceTest.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/PersonResourceTest.java similarity index 87% rename from integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/PersonResourceTest.java rename to integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/PersonResourceTest.java index 1b84f2b79d..ced22f52c6 100644 --- a/integration/jaxrs/src/test/java/com/blazebit/persistence/integration/jaxrs/testsuite/PersonResourceTest.java +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/PersonResourceTest.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.blazebit.persistence.integration.jaxrs.testsuite; +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite; -import com.blazebit.persistence.integration.jaxrs.testsuite.entity.Person; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.PersonCreateView; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.PersonUpdateView; -import com.blazebit.persistence.integration.jaxrs.testsuite.view.PersonView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Person; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.PersonCreateView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.PersonUpdateView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.PersonView; import org.junit.Test; import javax.ws.rs.client.Entity; diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/CriteriaBuilderFactoryProducer.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/CriteriaBuilderFactoryProducer.java new file mode 100644 index 0000000000..76720bfd72 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/CriteriaBuilderFactoryProducer.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.config; + +import com.blazebit.persistence.Criteria; +import com.blazebit.persistence.CriteriaBuilderFactory; +import com.blazebit.persistence.spi.CriteriaBuilderConfiguration; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Initialized; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.persistence.EntityManagerFactory; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@ApplicationScoped +public class CriteriaBuilderFactoryProducer { + // inject your entity manager factory + @Inject + private EntityManagerFactory entityManagerFactory; + + private volatile CriteriaBuilderFactory criteriaBuilderFactory; + + public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { + // no-op to force eager initialization + } + + @PostConstruct + public void createCriteriaBuilderFactory() { + CriteriaBuilderConfiguration config = Criteria.getDefault(); + // do some configuration + this.criteriaBuilderFactory = config.createCriteriaBuilderFactory(entityManagerFactory); + } + + @Produces + @ApplicationScoped + public CriteriaBuilderFactory produceCriteriaBuilderFactory() { + return criteriaBuilderFactory; + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerFactoryHolder.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerFactoryHolder.java new file mode 100644 index 0000000000..54665fd266 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerFactoryHolder.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.config; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +/** + * Workaround for WELD-2245 which prevents the use of @ApplicationScoped for EntityManagerFactory directly. + * + * @author Moritz Becker + * @since 1.6.4 + */ +@ApplicationScoped +public class EntityManagerFactoryHolder { + + private EntityManagerFactory emf; + + @PostConstruct + public void init() { + this.emf = Persistence.createEntityManagerFactory("TestsuiteBase", null); + } + + @PreDestroy + public void destroy() { + if (emf.isOpen()) { + emf.close(); + } + } + + @Produces + public EntityManagerFactory getEntityManagerFactory() { + return emf; + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerHolder.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerHolder.java new file mode 100644 index 0000000000..6c05265aa5 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityManagerHolder.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.config; + +import org.apache.deltaspike.jpa.api.transaction.TransactionScoped; + +import javax.annotation.PostConstruct; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@TransactionScoped +public class EntityManagerHolder { + + private EntityManager em; + + @Inject + @PostConstruct + public void init(EntityManagerFactory emf) { + em = emf.createEntityManager(); + } + + @Produces + public EntityManager getEntityManager() { + return em; + } + + public void dispose(@Disposes EntityManager entityManager) { + if (entityManager.isOpen()) { + entityManager.close(); + } + } +} \ No newline at end of file diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityViewManagerProducer.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityViewManagerProducer.java new file mode 100644 index 0000000000..18d627d2ac --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/config/EntityViewManagerProducer.java @@ -0,0 +1,79 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.config; + +import com.blazebit.persistence.CriteriaBuilderFactory; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.spi.type.TypeConverter; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Initialized; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import java.lang.reflect.Type; +import java.util.UUID; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@ApplicationScoped +public class EntityViewManagerProducer { + + // inject the configuration provided by the cdi integration + @Inject + private EntityViewConfiguration evmConfig; + + @Inject + private CriteriaBuilderFactory criteriaBuilderFactory; + + private EntityViewManager evm; + + public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { + // no-op to force eager initialization + } + + @PostConstruct + public void createEntityViewManager() { + evmConfig.registerTypeConverter(String.class, UUID.class, new TypeConverter() { + @Override + public Class getUnderlyingType(Class owningClass, Type declaredType) { + return String.class; + } + + @Override + public UUID convertToViewType(String object) { + return object == null ? null : UUID.fromString(object); + } + + @Override + public String convertToUnderlyingType(UUID object) { + return object == null ? null : object.toString(); + } + }); + evm = evmConfig.createEntityViewManager(criteriaBuilderFactory); + } + + @Produces + @ApplicationScoped + public EntityViewManager produceEntityViewManager() { + return evm; + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Document.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Document.java new file mode 100644 index 0000000000..d3a0fa7b91 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Document.java @@ -0,0 +1,88 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import java.io.Serializable; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@Entity +public class Document implements Serializable { + + private Long id; + private String name; + private String description; + private long age; + private Person owner; + + public Document() { + } + + public Document(String name) { + this.name = name; + } + + @Id + @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public long getAge() { + return age; + } + + public void setAge(long age) { + this.age = age; + } + + @ManyToOne(fetch = FetchType.LAZY) + public Person getOwner() { + return owner; + } + + public void setOwner(Person owner) { + this.owner = owner; + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Person.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Person.java new file mode 100644 index 0000000000..e3907e7afa --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/entity/Person.java @@ -0,0 +1,90 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author Christian Beikov + * @author Moritz Becker + * @since 1.6.4 + */ +@Entity +public class Person implements Serializable { + private static final long serialVersionUID = 1L; + + private String id; + private String name; + private long age; + private Set documents = new HashSet<>(0); + + public Person() { + id = UUID.randomUUID().toString(); + } + + public Person(String name) { + this(); + this.name = name; + } + + public Person(String name, long age) { + this(name); + this.age = age; + } + + @Id + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Basic(optional = false) + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getAge() { + return age; + } + + public void setAge(long age) { + this.age = age; + } + + @OneToMany(mappedBy = "owner") + public Set getDocuments() { + return documents; + } + + public void setDocuments(Set documents) { + this.documents = documents; + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/Application.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/Application.java new file mode 100644 index 0000000000..285d0923b5 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/Application.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.resource; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.JsonbJsonProvider; +import org.glassfish.jersey.server.ResourceConfig; + +import javax.enterprise.inject.Alternative; +import javax.ws.rs.ApplicationPath; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@Alternative +@ApplicationPath("/") +public class Application extends ResourceConfig { + + public Application() { + this.packages("com.blazebit.persistence.integration.jaxrs.jsonb"); + this.register(JsonbJsonProvider.class); + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/DocumentResource.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/DocumentResource.java new file mode 100644 index 0000000000..a2830ae963 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/DocumentResource.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.resource; + +import com.blazebit.persistence.integration.jaxrs.EntityViewId; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.config.EntityManagerHolder; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.DocumentUpdateView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.DocumentView; +import com.blazebit.persistence.view.EntityViewManager; +import org.apache.deltaspike.jpa.api.transaction.Transactional; + +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import java.util.Objects; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@Path("documents") +public class DocumentResource { + + @Inject + private EntityManagerHolder emHolder; + @Inject + private EntityViewManager evm; + + @Transactional + @PUT + @Path("{id1}") + @Consumes("application/vnd.blazebit.update1+json") + @Produces(MediaType.APPLICATION_JSON) + public DocumentView updateDocument1(@EntityViewId("id1") DocumentUpdateView documentUpdateView) { + return updateDocument0(documentUpdateView); + } + + @Transactional + @PUT + @Path("{id2}") + @Consumes("application/vnd.blazebit.update2+json") + @Produces(MediaType.APPLICATION_JSON) + public DocumentView updateDocument2(@PathParam(value = "id2") Long documentId, @EntityViewId("id2") DocumentUpdateView documentUpdate) { + Objects.requireNonNull(documentId); + return updateDocument0(documentUpdate); + } + + @Transactional + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public DocumentView updateDocument3(DocumentUpdateView documentUpdate) { + return updateDocument0(documentUpdate); + } + + private DocumentView updateDocument0(DocumentUpdateView documentUpdateView) { + evm.save(emHolder.getEntityManager(), documentUpdateView); + return evm.find(emHolder.getEntityManager(), DocumentView.class, documentUpdateView.getId()); + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/PersonResource.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/PersonResource.java new file mode 100644 index 0000000000..7cd80f05d0 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/resource/PersonResource.java @@ -0,0 +1,71 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.resource; + +import com.blazebit.persistence.integration.jaxrs.EntityViewId; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.config.EntityManagerHolder; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.PersonCreateView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.PersonUpdateView; +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view.PersonView; +import com.blazebit.persistence.view.EntityViewManager; +import org.apache.deltaspike.jpa.api.transaction.Transactional; + +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.net.URI; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@Path("persons") +public class PersonResource { + + @Inject + private EntityManagerHolder entityManagerHolder; + @Inject + private EntityViewManager evm; + + @Transactional + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response createPerson(PersonCreateView personCreateView) { + evm.save(entityManagerHolder.getEntityManager(), personCreateView); + return Response.created(URI.create("/persons/" + personCreateView.getId())).build(); + } + + @Transactional + @PUT + @Path("{id}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public PersonView updatePerson(@EntityViewId("id") PersonUpdateView personUpdate) { + return updatePerson0(personUpdate); + } + + private PersonView updatePerson0(PersonUpdateView personUpdate) { + evm.save(entityManagerHolder.getEntityManager(), personUpdate); + return evm.find(entityManagerHolder.getEntityManager(), PersonView.class, personUpdate.getId().toString()); + } +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentUpdateView.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentUpdateView.java new file mode 100644 index 0000000000..6699b99e66 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentUpdateView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Document; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.UpdatableEntityView; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@UpdatableEntityView +@EntityView(Document.class) +public interface DocumentUpdateView { + + @IdMapping + Long getId(); + + String getName(); + void setName(String name); +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentView.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentView.java new file mode 100644 index 0000000000..6aa791960b --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/DocumentView.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Document; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.Mapping; +import com.blazebit.persistence.view.MappingParameter; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@EntityView(Document.class) +public interface DocumentView { + + @IdMapping + Long getId(); + + String getName(); + + PersonView getOwner(); + + @Mapping("size(owner.documents)") + long getOwnerDocumentCount(); + + @MappingParameter("optionalParameter") + String getOptionalParameter(); +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonCreateView.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonCreateView.java new file mode 100644 index 0000000000..e8276cc313 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonCreateView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Person; +import com.blazebit.persistence.view.CreatableEntityView; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@CreatableEntityView +@EntityView(Person.class) +public interface PersonCreateView { + + @IdMapping + String getId(); + void setId(String id); + + String getName(); + void setName(String name); +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonUpdateView.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonUpdateView.java new file mode 100644 index 0000000000..e687fbfb56 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonUpdateView.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Person; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.UpdatableEntityView; + +import java.util.UUID; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@UpdatableEntityView +@EntityView(Person.class) +public interface PersonUpdateView { + + @IdMapping + UUID getId(); + + String getName(); + void setName(String name); +} diff --git a/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonView.java b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonView.java new file mode 100644 index 0000000000..853b9182b7 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/java/com/blazebit/persistence/integration/jaxrs/jsonb/testsuite/view/PersonView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.view; + +import com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Person; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; + +import java.util.UUID; + +/** + * @author Moritz Becker + * @since 1.6.4 + */ +@EntityView(Person.class) +public interface PersonView { + + @IdMapping + UUID getId(); + + String getName(); + +} diff --git a/integration/jaxrs-jsonb/src/test/resources/META-INF/beans.xml b/integration/jaxrs-jsonb/src/test/resources/META-INF/beans.xml new file mode 100644 index 0000000000..c690a9b5cc --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/resources/META-INF/beans.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/integration/jaxrs/src/test/resources/META-INF/persistence.xml b/integration/jaxrs-jsonb/src/test/resources/META-INF/persistence.xml similarity index 88% rename from integration/jaxrs/src/test/resources/META-INF/persistence.xml rename to integration/jaxrs-jsonb/src/test/resources/META-INF/persistence.xml index 881213c39d..7d32e1c265 100644 --- a/integration/jaxrs/src/test/resources/META-INF/persistence.xml +++ b/integration/jaxrs-jsonb/src/test/resources/META-INF/persistence.xml @@ -17,8 +17,8 @@ - com.blazebit.persistence.integration.jaxrs.testsuite.entity.Document - com.blazebit.persistence.integration.jaxrs.testsuite.entity.Person + com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Document + com.blazebit.persistence.integration.jaxrs.jsonb.testsuite.entity.Person true diff --git a/integration/jaxrs-jsonb/src/test/resources/logging.properties b/integration/jaxrs-jsonb/src/test/resources/logging.properties new file mode 100644 index 0000000000..1fd8cc2ef2 --- /dev/null +++ b/integration/jaxrs-jsonb/src/test/resources/logging.properties @@ -0,0 +1,43 @@ +# +# Copyright 2014 - 2021 Blazebit. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +handlers = java.util.logging.ConsoleHandler + +# For debugging purposes use ALL +# We turn it off for TravisCI build with SEVERE +org.hibernate.level = SEVERE +org.hibernate.tool.hbm2ddl.level = OFF +#org.hibernate.SQL.level = ALL +#org.hibernate.type.descriptor.sql.level = ALL +#org.hibernate.tool.hbm2ddl.level = ALL +#org.hibernate.pretty.level = ALL +#org.hibernate.cache.level = ALL +#org.hibernate.hql.internal.ast.AST.level = ALL + +#DataNucleus.Datastore.Native.level = ALL + +org.eclipse.persistence.level = SEVERE +#org.eclipse.persistence.session.default.sql.level = SEVERE +#org.eclipse.persistence.session.default.sql.level = ALL + +com.blazebit.persistence.spi.EntityManagerFactoryIntegrator.level = OFF +com.blazebit.persistence.impl.datanucleus.function.DataNucleusEntityManagerFactoryIntegrator.level = OFF + +# Enable connection pool error logging +org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.level = SEVERE + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter \ No newline at end of file diff --git a/integration/jaxrs/pom.xml b/integration/jaxrs/pom.xml index c60a68e63e..3e4af45779 100644 --- a/integration/jaxrs/pom.xml +++ b/integration/jaxrs/pom.xml @@ -14,179 +14,8 @@ com.blazebit.persistence.integration.jaxrs - - UTF-8 - 1.8 - 1.8 - - 1.9.3 - 5.4.6.Final - 5.4 - 2.30.1 - 2.9.8 - 2.1.1 - - - - org.apache.deltaspike.distribution - distributions-bom - ${version.deltaspike} - pom - import - - - - - - - ${project.groupId} - blaze-persistence-entity-view-api - - - com.fasterxml.jackson.core - jackson-databind - ${version.jackson} - provided - - - ${project.groupId} - blaze-persistence-integration-jackson - - - ${project.groupId} - blaze-common-utils - - - javax.ws.rs - javax.ws.rs-api - ${version.jaxrs} - provided - - - org.glassfish.jersey.core - jersey-common - ${version.jersey} - true - - - - - org.hibernate.javax.persistence - hibernate-jpa-2.1-api - 1.0.0.Final - provided - - - javax.enterprise - cdi-api - 2.0.SP1 - provided - - - jakarta.annotation - jakarta.annotation-api - ${version.annotation} - provided - - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - ${version.jackson} - test - - - com.blazebit - blaze-persistence-integration-entity-view-cdi - test - - - com.blazebit - blaze-persistence-core-impl - test - - - com.blazebit - blaze-persistence-entity-view-impl - test - - - ${project.groupId} - blaze-persistence-testsuite-base-jpa - test - - - com.blazebit - blaze-persistence-integration-hibernate-${version.integration-hibernate} - test - - - org.hibernate - hibernate-entitymanager - ${version.hibernate} - test - - - org.apache.deltaspike.modules - deltaspike-jpa-module-impl - test - - - org.glassfish.jersey.core - jersey-server - ${version.jersey} - test - - - org.glassfish.jersey.containers - jersey-container-jetty-http - ${version.jersey} - test - - - org.glassfish.jersey.inject - jersey-cdi2-se - ${version.jersey} - test - - - org.jboss - jandex - - - javax.enterprise - cdi-api - - - - - com.h2database - h2 - test - - - junit - junit - test - - - - - jakarta.xml.bind - jakarta.xml.bind-api - ${version.jaxb-api} - test - - - com.sun.xml.bind - jaxb-impl - ${version.jaxb} - test - - - @@ -203,11 +32,7 @@ module ${module.name} { - requires transitive com.blazebit.persistence.view; - requires com.blazebit.common.utils; - requires com.blazebit.persistence.integration.jackson; exports com.blazebit.persistence.integration.jaxrs; - provides org.glassfish.jersey.model.internal.spi.ParameterServiceProvider with com.blazebit.persistence.integration.jaxrs.jersey.EntityViewIdAwareParameterServiceProvider; } @@ -215,35 +40,7 @@ - - org.apache.maven.plugins - maven-surefire-plugin - - - com.blazebit.persistence.testsuite.base.jpa.category.NoH2,com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate - - - - - org.apache.maven.surefire - surefire-junit47 - ${version.surefire.plugin} - - - - - - post-10-unsafe-code - - [11,) - - - --add-opens java.base/java.lang=ALL-UNNAMED - - - - \ No newline at end of file diff --git a/integration/jaxrs/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider b/integration/jaxrs/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider deleted file mode 100644 index f73e9f3e58..0000000000 --- a/integration/jaxrs/src/main/resources/META-INF/services/org.glassfish.jersey.model.internal.spi.ParameterServiceProvider +++ /dev/null @@ -1 +0,0 @@ -com.blazebit.persistence.integration.jaxrs.jersey.EntityViewIdAwareParameterServiceProvider \ No newline at end of file diff --git a/integration/jsonb-jakarta/pom.xml b/integration/jsonb-jakarta/pom.xml new file mode 100644 index 0000000000..cca47d087a --- /dev/null +++ b/integration/jsonb-jakarta/pom.xml @@ -0,0 +1,167 @@ + + + + + 4.0.0 + + + com.blazebit + blaze-persistence-integration + 1.6.4-SNAPSHOT + ../pom.xml + + + blaze-persistence-integration-jsonb-jakarta + jar + + + + ${project.groupId} + blaze-persistence-integration-jsonb + provided + + + jakarta.persistence + jakarta.persistence-api + ${version.jakarta-jpa-api} + + + ${project.groupId} + blaze-persistence-entity-view-api-jakarta + + + ${project.groupId} + blaze-common-utils + + + + + + + maven-antrun-plugin + + + transform-jar + package + + run + + + + + + + + + + + + + + + + + + + + + + transform-sources-jar + package + + run + + + + + + + + + + + + + + + + + + + + + + + + transform-javadoc + package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + org.eclipse.transformer + org.eclipse.transformer.cli + 0.2.0 + + + ant-contrib + ant-contrib + 1.0b3 + + + ant + ant + + + + + + + + + diff --git a/integration/jsonb/pom.xml b/integration/jsonb/pom.xml new file mode 100644 index 0000000000..16104b8c4e --- /dev/null +++ b/integration/jsonb/pom.xml @@ -0,0 +1,105 @@ + + + + blaze-persistence-integration + com.blazebit + 1.6.4-SNAPSHOT + ../pom.xml + + 4.0.0 + + blaze-persistence-integration-jsonb + + Blazebit Persistence Integration JSONB + + + 1.8 + 1.8 + com.blazebit.persistence.integration.jsonb + + + + + ${project.groupId} + blaze-persistence-entity-view-api + + + ${project.groupId} + blaze-persistence-entity-view-impl + provided + + + jakarta.json.bind + jakarta.json.bind-api + 1.0.2 + provided + + + org.eclipse + yasson + 1.0.9 + provided + + + ${project.groupId} + blaze-common-utils + + + javax + javaee-api + 7.0 + provided + + + ${project.groupId} + blaze-persistence-core-impl + test + + + org.hibernate + hibernate-entitymanager + ${version.hibernate-5.4} + test + + + ${project.groupId} + blaze-persistence-integration-hibernate-5.4 + test + + + junit + junit + test + + + + + + + org.moditect + moditect-maven-plugin + + + add-module-infos + package + + add-module-info + + + + + module ${module.name} { + requires transitive com.blazebit.persistence.view; + requires com.blazebit.common.utils; + exports com.blazebit.persistence.integration.jackson; + } + + + + + + + + + + \ No newline at end of file diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewIdValueAccessor.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewIdValueAccessor.java new file mode 100644 index 0000000000..8b63d1f2d7 --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewIdValueAccessor.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jsonb; + + +import javax.json.bind.serializer.DeserializationContext; +import javax.json.stream.JsonParser; + +/** + * This interface is used to supply an entity view id from platform specific sources + * to the deserializer. The deserializer uses values provided by {@link EntityViewIdValueAccessor} + * as fallback in case no ID value extraction via the {@link JsonParser} is possible. + * + * @author Christian Beikov + * @since 1.6.4 + */ +public interface EntityViewIdValueAccessor { + + /** + * Retrieve an ID value for the entity view that is being deserialized at invocation time. + * + * @param jsonParser The {@link JsonParser} instance used by the deserializer + * @param deserializationContext The deserialization context + * @param idType The ID type + * @param An ID type parameter + * @return The ID value or null + */ + T getValue(JsonParser jsonParser, DeserializationContext deserializationContext, Class idType); +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializer.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializer.java new file mode 100644 index 0000000000..bd66768a48 --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializer.java @@ -0,0 +1,123 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jsonb; + +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.metamodel.ManagedViewType; +import javassist.CannotCompileException; +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtNewConstructor; +import javassist.bytecode.SignatureAttribute; + +import javax.json.bind.JsonbConfig; +import javax.json.bind.serializer.DeserializationContext; +import javax.json.bind.serializer.JsonbDeserializer; +import javax.json.stream.JsonParser; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +public class EntityViewJsonbDeserializer implements JsonbDeserializer { + + private final Map, EntityViewReferenceDeserializer> deserializers; + + public EntityViewJsonbDeserializer(EntityViewManager entityViewManager, EntityViewIdValueAccessor entityViewIdValueAccessor) { + this(createDeserializers(entityViewManager, entityViewIdValueAccessor)); + } + + public EntityViewJsonbDeserializer(Map, EntityViewReferenceDeserializer> deserializers) { + this.deserializers = deserializers; + } + + public static Map, EntityViewReferenceDeserializer> createDeserializers(EntityViewManager entityViewManager, EntityViewIdValueAccessor entityViewIdValueAccessor) { + Map, EntityViewReferenceDeserializer> deserializers = new HashMap<>(); + for (ManagedViewType view : entityViewManager.getMetamodel().getManagedViews()) { + deserializers.put(view.getJavaType(), new EntityViewReferenceDeserializer(entityViewManager, view, entityViewIdValueAccessor)); + } + return deserializers; + } + + public static void integrate(JsonbConfig config, EntityViewManager entityViewManager) { + integrate(config, entityViewManager, EntityViewJsonbDeserializer.createDeserializers(entityViewManager, null)); + } + + public static void integrate(JsonbConfig config, EntityViewManager entityViewManager, EntityViewIdValueAccessor entityViewIdValueAccessor) { + integrate(config, entityViewManager, EntityViewJsonbDeserializer.createDeserializers(entityViewManager, entityViewIdValueAccessor)); + } + + public static void integrate(JsonbConfig config, EntityViewManager entityViewManager, Map, EntityViewReferenceDeserializer> deserializers) { + try { + config.withPropertyVisibilityStrategy(new EntityViewPropertyVisibilityStrategy(entityViewManager)); + // TODO: the deserializer class generation should be done through https://github.com/Blazebit/blaze-persistence/issues/1044 + ClassPool pool = new ClassPool((ClassPool) null); + pool.appendSystemPath(); + pool.insertClassPath(new ClassClassPath(EntityViewJsonbDeserializer.class)); + CtClass baseDeserializer = pool.get(EntityViewJsonbDeserializer.class.getName()); + for (Map.Entry, EntityViewReferenceDeserializer> entry : deserializers.entrySet()) { + Class viewClass = entry.getKey(); + pool.insertClassPath(new ClassClassPath(viewClass)); + CtClass deserializerClass = pool.makeClass(viewClass.getName() + "Deserializer", baseDeserializer); + SignatureAttribute.ClassType classType = new SignatureAttribute.ClassType(EntityViewJsonbDeserializer.class.getName(), new SignatureAttribute.TypeArgument[]{new SignatureAttribute.TypeArgument(new SignatureAttribute.ClassType(viewClass.getName()))}); + deserializerClass.getClassFile2().addAttribute(new SignatureAttribute(deserializerClass.getClassFile2().getConstPool(), new SignatureAttribute.ClassSignature(null, classType, null).encode())); + deserializerClass.addConstructor(CtNewConstructor.make(new CtClass[]{pool.get(Map.class.getName())}, new CtClass[0], deserializerClass)); + Class clazz = define(deserializerClass); + config.withDeserializers((JsonbDeserializer) clazz.getConstructor(Map.class).newInstance(deserializers)); + } + } catch (Exception ex) { + throw new RuntimeException("Couldn't register deserializers into JsonbConfig!", ex); + } + } + + private static Class define(CtClass deserializerClass) throws CannotCompileException { + try { + return deserializerClass.toClass(); + } catch (CannotCompileException | LinkageError ex) { + // If there are multiple proxy factories for the same class loader + // we could end up in defining a class multiple times, so we check if the classloader + // actually has something to offer + LinkageError error; + if (ex instanceof LinkageError && (error = (LinkageError) ex) != null + || ex.getCause() instanceof InvocationTargetException && ex.getCause().getCause() instanceof LinkageError && (error = (LinkageError) ex.getCause().getCause()) != null + || ex.getCause() instanceof LinkageError && (error = (LinkageError) ex.getCause()) != null) { + try { + return deserializerClass.getClassPool().getClassLoader().loadClass(deserializerClass.getName()); + } catch (ClassNotFoundException cnfe) { + // Something we can't handle happened + throw error; + } + } else { + throw ex; + } + } + } + + @Override + public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext, Type type) { + EntityViewReferenceDeserializer deserializer = deserializers.get(type); + if (deserializer == null) { + return null; + } + return deserializer.deserialize(jsonParser, deserializationContext); + } +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewPropertyVisibilityStrategy.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewPropertyVisibilityStrategy.java new file mode 100644 index 0000000000..cb5db5a85a --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewPropertyVisibilityStrategy.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jsonb; + +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.spi.type.EntityViewProxy; + +import javax.json.bind.annotation.JsonbTransient; +import javax.json.bind.config.PropertyVisibilityStrategy; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +public class EntityViewPropertyVisibilityStrategy implements PropertyVisibilityStrategy { + + private final EntityViewManager evm; + + public EntityViewPropertyVisibilityStrategy(EntityViewManager evm) { + this.evm = evm; + } + + @Override + public boolean isVisible(Field field) { + return false; + } + + @Override + public boolean isVisible(Method method) { + if (EntityViewProxy.class.isAssignableFrom(method.getDeclaringClass())) { + Class superclass = method.getDeclaringClass().getSuperclass(); + if (superclass == Object.class) { + superclass = method.getDeclaringClass().getInterfaces()[0]; + } + try { + method = superclass.getMethod(method.getName(), method.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Couldn't find method on parent type", e); + } + } + return Modifier.isPublic(method.getModifiers()) && method.getAnnotation(JsonbTransient.class) == null; + } +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewReferenceDeserializer.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewReferenceDeserializer.java new file mode 100644 index 0000000000..3d2441b9da --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/EntityViewReferenceDeserializer.java @@ -0,0 +1,220 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jsonb; + +import com.blazebit.persistence.integration.jsonb.jsonstructure.JsonStructureToParserAdapter; +import com.blazebit.persistence.integration.jsonb.jsonstructure.JsonValueToParserAdapter; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.impl.metamodel.AbstractMethodAttribute; +import com.blazebit.persistence.view.metamodel.ManagedViewType; +import com.blazebit.persistence.view.metamodel.MethodAttribute; +import com.blazebit.persistence.view.metamodel.ViewType; +import org.eclipse.yasson.internal.JsonbRiParser; + +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonStructure; +import javax.json.JsonValue; +import javax.json.bind.annotation.JsonbProperty; +import javax.json.bind.annotation.JsonbTransient; +import javax.json.bind.serializer.DeserializationContext; +import javax.json.bind.spi.JsonbProvider; +import javax.json.stream.JsonParser; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +public class EntityViewReferenceDeserializer { + + private static final ThreadLocal STACK = new ThreadLocal<>(); + private static final boolean IS_YASSON = "org.eclipse.yasson".equals(JsonbProvider.provider().getClass().getPackage().getName()); + + private final EntityViewManager entityViewManager; + private final EntityViewIdValueAccessor entityViewIdValueAccessor; + private final Class entityViewClass; + private final String idAttributeName; + private final MethodAttribute idAttribute; + private final Class idType; + private final Map> attributes; + private final boolean deserializeIdFromJson; + private final boolean updatable; + private final boolean creatable; + + public EntityViewReferenceDeserializer(EntityViewManager entityViewManager, ManagedViewType view, EntityViewIdValueAccessor entityViewIdValueAccessor) { + this.entityViewManager = entityViewManager; + this.entityViewClass = view.getJavaType(); + this.entityViewIdValueAccessor = entityViewIdValueAccessor; + if (view instanceof ViewType) { + this.idAttribute = ((ViewType) view).getIdAttribute(); + this.deserializeIdFromJson = true; + JsonbProperty jsonbProperty = idAttribute.getJavaMethod().getAnnotation(JsonbProperty.class); + String name; + if (jsonbProperty == null) { + name = idAttribute.getName(); + } else { + name = jsonbProperty.value(); + } + this.idAttributeName = name; + this.idType = idAttribute.getConvertedJavaType(); + } else { + this.idAttribute = null; + this.idAttributeName = null; + this.idType = null; + this.deserializeIdFromJson = false; + } + this.updatable = view.isUpdatable(); + this.creatable = view.isCreatable(); + Map> attributes = new HashMap<>(view.getAttributes().size()); + for (MethodAttribute attribute : view.getAttributes()) { + if (attribute.getJavaMethod().getAnnotation(JsonbTransient.class) != null) { + continue; + } + JsonbProperty jsonbProperty = attribute.getJavaMethod().getAnnotation(JsonbProperty.class); + String name; + if (jsonbProperty == null) { + name = attribute.getName(); + } else { + name = jsonbProperty.value(); + } + attributes.put(name, (AbstractMethodAttribute) attribute); + } + this.attributes = attributes; + } + + public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) { + boolean isRoot = STACK.get() == null; + try { + if (isRoot) { + STACK.set(this); + } + Object reference = null; + JsonObject jsonStructure = deserializationContext.deserialize(JsonObject.class, jsonParser); + boolean idConsumed = false; + Object id; + if (idAttributeName == null || idType == null) { + id = null; + } else { + JsonValue jsonNode; + if (deserializeIdFromJson && (jsonNode = jsonStructure.get(idAttributeName)) != null) { + if (jsonNode.getValueType() == JsonValue.ValueType.NULL) { + id = null; + } else { + id = deserializationContext.deserialize(idType, parser(jsonNode)); + // Consume (i.e. remove from the payload json tree) the id if we are going to use getReference + if (!creatable || updatable) { + idConsumed = true; + } + } + } else if (isRoot && entityViewIdValueAccessor != null) { + id = entityViewIdValueAccessor.getValue(jsonParser, deserializationContext, idType); + } else { + id = null; + } + } + + // We create also creatable & updatable views if no id is given + // If an id is given in such a case, we create a reference for updates + if (creatable && (!updatable || id == null)) { + reference = entityViewManager.create(entityViewClass); + } else if (id != null) { + reference = entityViewManager.getReference(entityViewClass, id); + } + + if (reference == null) { + return null; + } + + try { + for (Map.Entry entry : jsonStructure.entrySet()) { + AbstractMethodAttribute attribute = attributes.get(entry.getKey()); + if (attribute == null) { + throw new RuntimeException("Unknown attribute [" + entry.getKey() + "]"); + } + if (attribute == idAttribute) { + if (idConsumed) { + continue; + } else if (id != null) { + attribute.getSetterMethod().invoke(reference, id); + continue; + } + } + JsonValue jsonValue = entry.getValue(); + if (attribute.isCollection()) { + Type elementType = attribute.getElementType().getConvertedType(); + if (elementType == null) { + elementType = attribute.getElementType().getJavaType(); + } + Object container = attribute.getJavaMethod().invoke(reference); + JsonArray jsonValues = jsonValue.asJsonArray(); + if (container instanceof Map) { + Map map = (Map) container; + for (JsonValue entryValue : jsonValues) { + JsonObject entryObject = entryValue.asJsonObject(); + if (entryObject.size() != 1) { + throw new RuntimeException("Unexpected non-entry like element [" + entryObject + "]"); + } + Map.Entry entryValueEntry = entryObject.entrySet().iterator().next(); + map.put(entryValueEntry.getKey(), deserializationContext.deserialize(elementType, parser(entryValueEntry.getValue()))); + } + } else { + Collection collection = (Collection) container; + for (JsonValue value : jsonValues) { + collection.add(deserializationContext.deserialize(elementType, parser(value))); + } + } + } else { + if (attribute.getSetterMethod() == null) { + throw new RuntimeException("Unexpected non-updatable attribute [" + entry.getKey() + "]"); + } + Type elementType = attribute.getElementType().getConvertedType(); + if (elementType == null) { + elementType = attribute.getElementType().getJavaType(); + } + Object value = deserializationContext.deserialize(elementType, parser(jsonValue)); + attribute.getSetterMethod().invoke(reference, value); + } + } + } catch (Exception ex) { + throw new RuntimeException("Couldn't deserialize: " + jsonStructure, ex); + } + + return (T) reference; + } finally { + if (isRoot) { + STACK.remove(); + } + } + } + + private static JsonParser parser(JsonValue jsonValue) { + JsonParser newParser; + if (jsonValue instanceof JsonStructure) { + newParser = new JsonStructureToParserAdapter((JsonStructure) jsonValue); + } else { + newParser = new JsonValueToParserAdapter(jsonValue); + } + if (IS_YASSON) { + return new JsonbRiParser(newParser); + } + return newParser; + } +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonArrayIterator.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonArrayIterator.java new file mode 100644 index 0000000000..8227e76ac8 --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonArrayIterator.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package com.blazebit.persistence.integration.jsonb.jsonstructure; + +import javax.json.JsonArray; +import javax.json.JsonString; +import javax.json.JsonValue; +import javax.json.bind.JsonbException; +import javax.json.stream.JsonParser; +import java.util.Iterator; + +/** + * Iterates over {@link JsonArray}. + */ +public class JsonArrayIterator extends JsonStructureIterator { + + private final JsonArray jsonArray; + private final Iterator valueIterator; + + private JsonValue currentValue; + + /** + * Creates new array iterator. + * + * @param jsonArray json array + */ + public JsonArrayIterator(JsonArray jsonArray) { + this.jsonArray = jsonArray; + this.valueIterator = jsonArray.iterator(); + } + + /** + * After {@link JsonParser.Event} END_ARRAY is returned from next() iterator is removed from the stack. + * + * @return always true + */ + @Override + public boolean hasNext() { + return true; + } + + @Override + public JsonParser.Event next() { + if (valueIterator.hasNext()) { + currentValue = valueIterator.next(); + return getValueEvent(currentValue); + } + return JsonParser.Event.END_ARRAY; + } + + @Override + JsonValue getValue() { + return currentValue; + } + + @Override + JsonbException createIncompatibleValueError() { + return new JsonbException("Incompatible array type: " + getValue().getValueType()); + } + + @Override + String getString() { + if (currentValue instanceof JsonString) { + return ((JsonString) currentValue).getString(); + } + return currentValue.toString(); + } + + public JsonArray getJsonArray() { + return jsonArray; + } +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonObjectIterator.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonObjectIterator.java new file mode 100644 index 0000000000..0a5351703c --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonObjectIterator.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package com.blazebit.persistence.integration.jsonb.jsonstructure; + +import javax.json.JsonObject; +import javax.json.JsonValue; +import javax.json.bind.JsonbException; +import javax.json.stream.JsonParser; +import java.util.Iterator; + +/** + * Iterates over {@link JsonObject} managing internal state. + */ +public class JsonObjectIterator extends JsonStructureIterator { + + /** + * Location pointer. + */ + public enum State { + /** + * Start of the object. + */ + START, + /** + * Property key name. + */ + KEY, + /** + * Property value. + */ + VALUE, + /** + * End of the object. + */ + END + } + + private final JsonObject jsonObject; + + private final Iterator keyIterator; + + private String currentKey; + + private State state = State.START; + + JsonObjectIterator(JsonObject jsonObject) { + this.jsonObject = jsonObject; + this.keyIterator = jsonObject.keySet().iterator(); + } + + private void nextKey() { + if (!keyIterator.hasNext()) { + throw new JsonbException("Object is empty"); + } + currentKey = keyIterator.next(); + } + + @Override + public JsonParser.Event next() { + switch (state) { + case START: + if (keyIterator.hasNext()) { + nextKey(); + setState(State.KEY); + return JsonParser.Event.KEY_NAME; + } else { + setState(State.END); + return JsonParser.Event.END_OBJECT; + } + case KEY: + setState(State.VALUE); + JsonValue value = getValue(); + return getValueEvent(value); + case VALUE: + if (keyIterator.hasNext()) { + nextKey(); + setState(State.KEY); + return JsonParser.Event.KEY_NAME; + } + setState(State.END); + return JsonParser.Event.END_OBJECT; + default: + throw new JsonbException("Illegal state"); + } + + } + + @Override + public boolean hasNext() { + //From the perspective of JsonParser not finished until END_OBJECT is being read. + return state != State.END; + } + + /** + * {@link JsonValue} for current key. + * + * @return Current JsonValue. + */ + public JsonValue getValue() { + return jsonObject.get(currentKey); + } + + @Override + String getString() { + if (state == State.KEY) { + return currentKey; + } + return super.getString(); + } + + @Override + JsonbException createIncompatibleValueError() { + return new JsonbException("Incompatible value type [" + getValue().getValueType() + "] for key: " + currentKey); + } + + private void setState(State state) { + this.state = state; + } + + /** + * Current key this iterator is pointing at. + * + * @return Current key. + */ + public String getKey() { + return currentKey; + } + + public JsonObject getJsonObject() { + return jsonObject; + } +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureIterator.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureIterator.java new file mode 100644 index 0000000000..58a66e162a --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureIterator.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package com.blazebit.persistence.integration.jsonb.jsonstructure; + +import javax.json.JsonString; +import javax.json.JsonValue; +import javax.json.bind.JsonbException; +import javax.json.stream.JsonParser; +import java.util.Iterator; + +/** + * Iterates over {@link javax.json.JsonStructure}. + */ +abstract class JsonStructureIterator implements Iterator { + + /** + * Get current {@link JsonValue}, that the parser is pointing on. + * + * @return JsonValue result. + */ + abstract JsonValue getValue(); + + /** + * Creates an exception for throwing in case of current value type is not compatible with + * called getter return type. + * + * @return JsonbException with error description. + */ + abstract JsonbException createIncompatibleValueError(); + + /** + * Check the type of current {@link JsonValue} and return a string representing a value. + * + * @return String value for current JsonValue + */ + String getString() { + JsonValue value = getValue(); + if (value instanceof JsonString) { + return ((JsonString) value).getString(); + } else { + return value.toString(); + } + } + + /** + * Convert {@link JsonValue} type to {@link JsonParser.Event}. + * + * @param value JsonValue + * @return JsonParser event + */ + JsonParser.Event getValueEvent(JsonValue value) { + switch (value.getValueType()) { + case NUMBER: + return JsonParser.Event.VALUE_NUMBER; + case STRING: + case TRUE: + case FALSE: + return JsonParser.Event.VALUE_STRING; + case OBJECT: + return JsonParser.Event.START_OBJECT; + case ARRAY: + return JsonParser.Event.START_ARRAY; + case NULL: + return JsonParser.Event.VALUE_NULL; + default: + throw new JsonbException("unknown json value: " + value.getValueType()); + } + + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove"); + } +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureToParserAdapter.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureToParserAdapter.java new file mode 100644 index 0000000000..c8514fad28 --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonStructureToParserAdapter.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package com.blazebit.persistence.integration.jsonb.jsonstructure; + +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonStructure; +import javax.json.JsonValue; +import javax.json.bind.JsonbException; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import java.math.BigDecimal; +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * Adapter for {@link JsonParser}, that reads a {@link JsonStructure} content tree instead of JSON text. + * + * Yasson and jsonb API components are using {@link JsonParser} as its input API. + * This adapter allows deserialization of {@link JsonStructure} into java content tree using same components + * as when parsing JSON text. + */ +public class JsonStructureToParserAdapter implements JsonParser { + + private final Deque iterators = new ArrayDeque<>(); + + private final JsonStructure rootStructure; + + /** + * Creates new {@link JsonStructure} parser. + * + * @param structure json structure + */ + public JsonStructureToParserAdapter(JsonStructure structure) { + this.rootStructure = structure; + } + + @Override + public boolean hasNext() { + return iterators.peek().hasNext(); + } + + @Override + public Event next() { + if (iterators.isEmpty()) { + if (rootStructure instanceof JsonObject) { + iterators.push(new JsonObjectIterator((JsonObject) rootStructure)); + return Event.START_OBJECT; + } else if (rootStructure instanceof JsonArray) { + iterators.push(new JsonArrayIterator((JsonArray) rootStructure)); + return Event.START_ARRAY; + } + } + JsonStructureIterator current = iterators.peek(); + Event next = current.next(); + if (next == Event.START_OBJECT) { + iterators.push(new JsonObjectIterator((JsonObject) iterators.peek().getValue())); + } else if (next == Event.START_ARRAY) { + iterators.push(new JsonArrayIterator((JsonArray) iterators.peek().getValue())); + } else if (next == Event.END_OBJECT || next == Event.END_ARRAY) { + iterators.pop(); + } + return next; + } + + @Override + public String getString() { + return iterators.peek().getString(); + } + + @Override + public boolean isIntegralNumber() { + return getJsonNumberValue().isIntegral(); + } + + @Override + public int getInt() { + return getJsonNumberValue().intValueExact(); + } + + @Override + public long getLong() { + return getJsonNumberValue().longValueExact(); + } + + @Override + public BigDecimal getBigDecimal() { + return getJsonNumberValue().bigDecimalValue(); + } + + private JsonNumber getJsonNumberValue() { + JsonStructureIterator iterator = iterators.peek(); + JsonValue value = iterator.getValue(); + if (value.getValueType() != JsonValue.ValueType.NUMBER) { + throw iterator.createIncompatibleValueError(); + } + return (JsonNumber) value; + } + + @Override + public JsonObject getObject() { + JsonStructureIterator iterator = iterators.peek(); + JsonValue value = iterator.getValue(); + if (value == null) { + if (iterator instanceof JsonObjectIterator) { + return ((JsonObjectIterator) iterator).getJsonObject(); + } + return null; + } + else if (value.getValueType() != JsonValue.ValueType.OBJECT) { + throw iterator.createIncompatibleValueError(); + } + return (JsonObject) value; + } + + @Override + public JsonArray getArray() { + JsonStructureIterator iterator = iterators.peek(); + JsonValue value = iterator.getValue(); + if (value == null) { + if (iterator instanceof JsonArrayIterator) { + return ((JsonArrayIterator) iterator).getJsonArray(); + } + return null; + } + else if (value.getValueType() != JsonValue.ValueType.ARRAY) { + throw iterator.createIncompatibleValueError(); + } + return (JsonArray) value; + } + + @Override + public JsonValue getValue() { + JsonStructureIterator iterator = iterators.peek(); + JsonValue value = iterator.getValue(); + if (value == null) { + if (iterator instanceof JsonObjectIterator) { + return ((JsonObjectIterator) iterator).getJsonObject(); + } + if (iterator instanceof JsonArrayIterator) { + return ((JsonArrayIterator) iterator).getJsonArray(); + } + } + return value; + } + + @Override + public JsonLocation getLocation() { + throw new JsonbException("Operation not supported"); + } + + @Override + public void close() { + //noop + } +} diff --git a/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonValueToParserAdapter.java b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonValueToParserAdapter.java new file mode 100644 index 0000000000..7955e1198e --- /dev/null +++ b/integration/jsonb/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/JsonValueToParserAdapter.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package com.blazebit.persistence.integration.jsonb.jsonstructure; + +import javax.json.JsonNumber; +import javax.json.JsonString; +import javax.json.JsonValue; +import javax.json.bind.JsonbException; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import java.math.BigDecimal; +import java.util.NoSuchElementException; + +/** + * Adapter for {@link JsonParser}, that reads a {@link JsonValue} content tree instead of JSON text. + */ +public class JsonValueToParserAdapter implements JsonParser { + + private final JsonValue value; + private boolean hasNext; + + /** + * Creates new {@link JsonValue} parser. + * + * @param value json value + */ + public JsonValueToParserAdapter(JsonValue value) { + this.value = value; + this.hasNext = true; + } + + @Override + public boolean hasNext() { + return hasNext; + } + + @Override + public Event next() { + if (!hasNext) { + throw new NoSuchElementException(); + } + hasNext = false; + switch (value.getValueType()) { + case TRUE: + return Event.VALUE_TRUE; + case FALSE: + return Event.VALUE_FALSE; + case NUMBER: + return Event.VALUE_NUMBER; + case STRING: + return Event.VALUE_STRING; + case NULL: + return Event.VALUE_NULL; + default: + throw new UnsupportedOperationException("Unsupported value: " + value); + } + } + + @Override + public String getString() { + if (hasNext) { + throw new NoSuchElementException(); + } + + switch (value.getValueType()) { + case TRUE: + return "true"; + case FALSE: + return "false"; + case NUMBER: + return getJsonNumberValue().toString(); + case STRING: + return ((JsonString) value).getString(); + case NULL: + return "null"; + default: + throw new UnsupportedOperationException("Unsupported value: " + value); + } + } + + @Override + public boolean isIntegralNumber() { + return getJsonNumberValue().isIntegral(); + } + + @Override + public int getInt() { + return getJsonNumberValue().intValueExact(); + } + + @Override + public long getLong() { + return getJsonNumberValue().longValueExact(); + } + + @Override + public BigDecimal getBigDecimal() { + return getJsonNumberValue().bigDecimalValue(); + } + + private JsonNumber getJsonNumberValue() { + if (hasNext) { + throw new NoSuchElementException(); + } + if (value.getValueType() != JsonValue.ValueType.NUMBER) { + throw new UnsupportedOperationException("Unsupported value: " + value); + } + return (JsonNumber) value; + } + + @Override + public JsonLocation getLocation() { + throw new JsonbException("Operation not supported"); + } + + @Override + public void close() { + //noop + } +} diff --git a/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializerTest.java b/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializerTest.java new file mode 100644 index 0000000000..f474462194 --- /dev/null +++ b/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/EntityViewJsonbDeserializerTest.java @@ -0,0 +1,426 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jsonb; + +import com.blazebit.persistence.Criteria; +import com.blazebit.persistence.CriteriaBuilderFactory; +import com.blazebit.persistence.view.CreatableEntityView; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.MappingSingular; +import com.blazebit.persistence.view.UpdatableEntityView; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.spi.type.EntityViewProxy; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.json.JsonObject; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbConfig; +import javax.json.bind.annotation.JsonbTransient; +import javax.json.bind.serializer.DeserializationContext; +import javax.json.stream.JsonParser; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.*; + +/** + * @author Christian Beikov + * @since 1.6.3 + */ +public class EntityViewJsonbDeserializerTest { + + static EntityManagerFactory emf; + static CriteriaBuilderFactory cbf; + + @BeforeClass + public static void prepare() { + emf = Persistence.createEntityManagerFactory("Test"); + cbf = Criteria.getDefault().createCriteriaBuilderFactory(emf); + } + + static EntityViewManager evm(Class... classes) { + EntityViewConfiguration configuration = EntityViews.createDefaultConfiguration(); + for (Class clazz : classes) { + configuration.addEntityView(clazz); + } + + return configuration.createEntityViewManager(cbf); + } + + static Jsonb mapper(EntityViewManager evm) { + return mapper(evm, null); + } + + static Jsonb mapper(EntityViewManager evm, EntityViewIdValueAccessor idValueAccessor) { + try { + JsonbConfig jsonbConfig = new JsonbConfig() + .withFormatting(true) + .withNullValues(true) + .setProperty("jsonb.fail-on-unknown-properties", true); + EntityViewJsonbDeserializer.integrate(jsonbConfig, evm, EntityViewJsonbDeserializer.createDeserializers(evm, idValueAccessor)); + return JsonbBuilder.create(jsonbConfig); + } catch (Exception ex) { + throw new RuntimeException("Could register jsonb objects for deserialization!", ex); + } + } + + @Test + public void testReadView() throws Exception { + Jsonb mapper = mapper(evm(NameView.class)); + NameView view = mapper.fromJson("{\"id\": 1}", NameView.class); + assertEquals(1L, view.getId()); + Assert.assertNull(view.getName()); + } + + @Test + public void testReadViewList() throws Exception { + Jsonb mapper = mapper(evm(NameView.class)); + ParameterizedType parameterizedType = new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return new Type[]{NameView.class}; + } + + @Override + public Type getRawType() { + return List.class; + } + + @Override + public Type getOwnerType() { + return null; + } + }; + List views = mapper.fromJson("[{\"id\": 1}, {\"id\": 2}]", parameterizedType); + assertEquals(2, views.size()); + assertEquals(1L, views.get(0).getId()); + Assert.assertNull(views.get(0).getName()); + assertEquals(2L, views.get(1).getId()); + Assert.assertNull(views.get(0).getName()); + } + + @EntityView(SomeEntity.class) + interface NameView { + @IdMapping + long getId(); + String getName(); + } + + @Test + public void testReadViewWithSetters() throws Exception { + Jsonb mapper = mapper(evm(ReadViewWithSetters.class, NameView.class)); + ReadViewWithSetters view = mapper.fromJson("{\"id\": 1, \"name\": \"test\", \"parent\": {\"id\": 2}}", ReadViewWithSetters.class); + assertEquals(1L, view.getId()); + assertEquals("test", view.getName()); + assertEquals(2L, view.getParent().getId()); + } + + @EntityView(SomeEntity.class) + interface ReadViewWithSetters { + @IdMapping + long getId(); + String getName(); + void setName(String name); + NameView getParent(); + void setParent(NameView parent); + } + + @Test + public void testUpdatableView() throws Exception { + Jsonb mapper = mapper(evm(UpdateViewWithSetters.class, NameView.class)); + UpdateViewWithSetters view = mapper.fromJson("{\"id\": 1, \"name\": \"test\", \"parent\": {\"id\": 2}}", UpdateViewWithSetters.class); + assertEquals(1L, view.getId()); + assertEquals("test", view.getName()); + assertEquals(2L, view.getParent().getId()); + } + + @EntityView(SomeEntity.class) + @UpdatableEntityView + interface UpdateViewWithSetters { + @IdMapping + long getId(); + String getName(); + void setName(String name); + NameView getParent(); + void setParent(NameView parent); + } + + @Test + public void testCreatableView() throws Exception { + Jsonb mapper = mapper(evm(CreatableViewWithSetters.class, NameView.class)); + CreatableViewWithSetters view = mapper.fromJson("{\"name\": \"test\", \"parent\": {\"id\": 2}}", CreatableViewWithSetters.class); + Assert.assertTrue(((EntityViewProxy) view).$$_isNew()); + assertEquals("test", view.getName()); + assertEquals(2L, view.getParent().getId()); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + interface CreatableViewWithSetters { + @IdMapping + long getId(); + String getName(); + void setName(String name); + NameView getParent(); + void setParent(NameView parent); + } + + @Test + public void testCreatableAndUpdatableView() throws Exception { + Jsonb mapper = mapper(evm(CreatableAndUpdatableViewWithSetters.class, NameView.class)); + CreatableAndUpdatableViewWithSetters view = mapper.fromJson("{\"id\": 1, \"name\": \"test\", \"parent\": {\"id\": 2}}", CreatableAndUpdatableViewWithSetters.class); + assertFalse(((EntityViewProxy) view).$$_isNew()); + assertEquals(1L, view.getId()); + assertEquals("test", view.getName()); + assertEquals(2L, view.getParent().getId()); + } + + @Test + public void testCreatableAndUpdatableViewWithoutId() throws Exception { + Jsonb mapper = mapper(evm(CreatableAndUpdatableViewWithSetters.class, NameView.class)); + CreatableAndUpdatableViewWithSetters view = mapper.fromJson("{\"name\": \"test\", \"parent\": {\"id\": 2}}", CreatableAndUpdatableViewWithSetters.class); + Assert.assertTrue(((EntityViewProxy) view).$$_isNew()); + assertEquals("test", view.getName()); + assertEquals(2L, view.getParent().getId()); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + @UpdatableEntityView + interface CreatableAndUpdatableViewWithSetters { + @IdMapping + long getId(); + String getName(); + void setName(String name); + NameView getParent(); + void setParent(NameView parent); + } + + @Test + public void testCreatableAndUpdatableViewWithoutNestedUpdatableView() throws Exception { + Jsonb mapper = mapper(evm(CreatableAndUpdatableViewWithNested.class, CreatableAndUpdatableViewWithSetters.class, NameView.class)); + CreatableAndUpdatableViewWithNested view = mapper.fromJson("{\"name\": \"test\", \"parent\": {\"id\": 2, \"name\": \"parent\"}}", CreatableAndUpdatableViewWithNested.class); + Assert.assertTrue(((EntityViewProxy) view).$$_isNew()); + assertEquals("test", view.getName()); + assertEquals(2L, view.getParent().getId()); + assertEquals("parent", view.getParent().getName()); + } + + @Test + public void testCreatableAndUpdatableViewWithoutNestedUpdatableViewWithoutId() throws Exception { + Jsonb mapper = mapper(evm(CreatableAndUpdatableViewWithNested.class, CreatableAndUpdatableViewWithSetters.class, NameView.class)); + CreatableAndUpdatableViewWithNested view = mapper.fromJson("{\"name\": \"test\", \"parent\": {\"name\": \"parent\"}}", CreatableAndUpdatableViewWithNested.class); + Assert.assertTrue(((EntityViewProxy) view).$$_isNew()); + assertEquals("test", view.getName()); + Assert.assertTrue(((EntityViewProxy) view.getParent()).$$_isNew()); + assertEquals("parent", view.getParent().getName()); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + @UpdatableEntityView + interface CreatableAndUpdatableViewWithNested { + @IdMapping + long getId(); + String getName(); + void setName(String name); + CreatableAndUpdatableViewWithSetters getParent(); + void setParent(CreatableAndUpdatableViewWithSetters parent); + } + + @Test + public void testCreatableWithCollection() throws Exception { + EntityViewManager evm = evm(CreatableWithCollection.class, CreatableAndUpdatableViewWithSetters.class, NameView.class); + Jsonb mapper = mapper(evm); + CreatableWithCollection view = mapper.fromJson("{\"name\": \"test\", \"children\": [{\"name\": \"parent\"}]}", CreatableWithCollection.class); + Assert.assertTrue(((EntityViewProxy) view).$$_isNew()); + assertEquals("test", view.getName()); + assertEquals(evm.getChangeModel(view).get("children").getInitialState(), view.getChildren()); + assertEquals(1, view.getChildren().size()); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + @UpdatableEntityView + interface CreatableWithCollection { + @IdMapping + long getId(); + String getName(); + void setName(String name); + Set getChildren(); + } + + @Test + public void testCreatableWithCollectionWithSetter() throws Exception { + EntityViewManager evm = evm(CreatableWithCollectionWithSetter.class, CreatableAndUpdatableViewWithSetters.class, NameView.class); + Jsonb mapper = mapper(evm); + CreatableWithCollectionWithSetter view = mapper.fromJson("{\"name\": \"test\", \"children\": [{\"name\": \"parent\"}]}", CreatableWithCollectionWithSetter.class); + Assert.assertTrue(((EntityViewProxy) view).$$_isNew()); + assertEquals("test", view.getName()); + assertEquals(evm.getChangeModel(view).get("children").getInitialState(), view.getChildren()); + assertEquals(1, view.getChildren().size()); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + @UpdatableEntityView + interface CreatableWithCollectionWithSetter { + @IdMapping + long getId(); + String getName(); + void setName(String name); + Set getChildren(); + void setChildren(Set children); + } + + @Test + public void testCreatableWithIgnoreId() throws Exception { + Jsonb mapper = mapper(evm(CreatableWithIgnoreId.class)); + try { + mapper.fromJson("{\"id\": 1, \"name\": \"test\"}", CreatableWithIgnoreId.class); + Assert.fail("Expected failure"); + } catch (Exception ex) { + assertEquals("Unknown attribute [id]", ex.getCause().getCause().getMessage()); + } + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + interface CreatableWithIgnoreId { + @IdMapping + @JsonbTransient + long getId(); + String getName(); + void setName(String name); + } + + @Test + public void testUpdatableWithCollectionWithSetter() throws Exception { + Jsonb mapper = mapper(evm(UpdatableWithCollectionWithSetter.class, NameView.class)); + UpdatableWithCollectionWithSetter view = mapper.fromJson("{\"id\":1, \"name\": \"test\", \"children\": [{\"id\": 1}]}", UpdatableWithCollectionWithSetter.class); + assertFalse(((EntityViewProxy) view).$$_isNew()); + assertEquals(1L, view.getId()); + assertEquals("test", view.getName()); + assertEquals(1L, view.getChildren().iterator().next().getId()); + assertEquals(1, view.getChildren().size()); + } + + @EntityView(SomeEntity.class) + @UpdatableEntityView + interface UpdatableWithCollectionWithSetter { + @IdMapping + long getId(); + String getName(); + void setName(String name); + Set getChildren(); + void setChildren(Set children); + } + + @Test + public void testIdValueAccessorWithCollection() throws Exception { + EntityViewIdValueAccessor accessor = new EntityViewIdValueAccessor() { + @Override + public T getValue(JsonParser jsonParser, DeserializationContext deserializationContext, Class idType) { + return (T) (Object) 1L; + } + }; + Jsonb mapper = mapper(evm(UpdatableWithCollectionForIdAccessor.class, NameViewForIdAccessor.class), accessor); + UpdatableWithCollectionForIdAccessor view = mapper.fromJson("{\"name\": \"test\", \"children\": [{\"name\": \"The child\"}]}", UpdatableWithCollectionForIdAccessor.class); + assertFalse(((EntityViewProxy) view).$$_isNew()); + assertEquals(1L, view.getId()); + assertEquals("test", view.getName()); + assertNull(view.getChildren().iterator().next().getId()); + assertEquals("The child", view.getChildren().iterator().next().getName()); + assertEquals(1, view.getChildren().size()); + } + + @EntityView(SomeEntity.class) + @UpdatableEntityView + interface UpdatableWithCollectionForIdAccessor { + @IdMapping + long getId(); + String getName(); + void setName(String name); + Set getChildren(); + void setChildren(Set children); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + interface NameViewForIdAccessor { + @IdMapping + Long getId(); + String getName(); + void setName(String name); + } + + @Test + public void testJsonIgnore() throws Exception { + EntityViewManager evm = evm(ViewWithJsonIgnore.class); + Jsonb mapper = mapper(evm); + ViewWithJsonIgnore view = evm.create(ViewWithJsonIgnore.class); + view.setId(1L); + view.setName("Joe"); + JsonObject viewAsJsonTree = mapper.fromJson(mapper.toJson(view, ViewWithJsonIgnore.class), JsonObject.class); + assertEquals(1L, viewAsJsonTree.getJsonNumber("id").longValue()); + assertNull(viewAsJsonTree.get("name")); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + static abstract class ViewWithJsonIgnore { + @IdMapping + public abstract long getId(); + public abstract void setId(long id); + @JsonbTransient + public abstract String getName(); + public abstract void setName(String name); + } + + @Test + public void testSingularCollection() throws Exception { + Jsonb mapper = mapper(evm(ViewWithSingularCollection.class)); + ViewWithSingularCollection view = mapper.fromJson("{\"name\": \"Joe\", \"tags\": [\"t1\", \"t2\"]}", ViewWithSingularCollection.class); + Assert.assertTrue(((EntityViewProxy) view).$$_isNew()); + assertEquals("Joe", view.getName()); + assertEquals(2, view.getTags().size()); + assertEquals("t1", view.getTags().get(0)); + assertEquals("t2", view.getTags().get(1)); + } + + @EntityView(SomeEntity.class) + @CreatableEntityView + static interface ViewWithSingularCollection { + @IdMapping + long getId(); + String getName(); + void setName(String name); + @MappingSingular + List getTags(); + void setTags(List tags); + } +} diff --git a/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/SomeEntity.java b/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/SomeEntity.java new file mode 100644 index 0000000000..d1778c1670 --- /dev/null +++ b/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/SomeEntity.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jsonb; + +import javax.persistence.Basic; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import java.util.List; +import java.util.Set; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Entity +public class SomeEntity { + @Id + Long id; + String name; + @ManyToOne(fetch = FetchType.LAZY) + SomeEntity parent; + @Basic + @Convert(converter = StringListConverter.class) + List tags; + @OneToMany(mappedBy = "parent") + Set children; +} diff --git a/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/StringListConverter.java b/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/StringListConverter.java new file mode 100644 index 0000000000..d7ab1c72af --- /dev/null +++ b/integration/jsonb/src/test/java/com/blazebit/persistence/integration/jsonb/StringListConverter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014 - 2021 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.integration.jsonb; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Christian Beikov + * @since 1.6.4 + */ +@Converter +public class StringListConverter implements AttributeConverter, String> { + @Override + public String convertToDatabaseColumn(List value) { + if (value == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + int size = value.size(); + sb.append(value.get(0)); + for (int i = 1; i < size; i++) { + sb.append(',').append(value.get(i)); + } + return sb.toString(); + } + + @Override + public List convertToEntityAttribute(String dbData) { + if (dbData == null) { + return null; + } + return new ArrayList<>(Arrays.asList(dbData.split(","))); + } +} diff --git a/integration/jsonb/src/test/resources/META-INF/persistence.xml b/integration/jsonb/src/test/resources/META-INF/persistence.xml new file mode 100644 index 0000000000..da87c50872 --- /dev/null +++ b/integration/jsonb/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,24 @@ + + + + + com.blazebit.persistence.integration.jsonb.SomeEntity + + + + + diff --git a/integration/pom.xml b/integration/pom.xml index c9e1f4bd98..69da23faae 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -50,8 +50,12 @@ spring-hateoas deltaspike-data jaxrs + jaxrs-jackson + jaxrs-jsonb jackson + jsonb graphql + graphql-spqr querydsl quarkus @@ -63,8 +67,11 @@ hibernate-base-jakarta hibernate-5.4-jakarta entity-view-cdi-jakarta - jaxrs-jakarta + jaxrs-jackson-jakarta + jaxrs-jsonb-jakarta jackson-jakarta + jsonb-jakarta graphql-jakarta + graphql-spqr-jakarta diff --git a/integration/quarkus/deployment/pom.xml b/integration/quarkus/deployment/pom.xml index d8426432ec..ab4a4ddacb 100644 --- a/integration/quarkus/deployment/pom.xml +++ b/integration/quarkus/deployment/pom.xml @@ -67,7 +67,7 @@ com.blazebit - blaze-persistence-integration-jaxrs + blaze-persistence-integration-jaxrs-jackson test diff --git a/parent/pom.xml b/parent/pom.xml index 9016579981..eefd96b7c1 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -517,7 +517,22 @@ ${project.groupId} - blaze-persistence-integration-jaxrs-jakarta + blaze-persistence-integration-jaxrs-jackson + ${project.version} + + + ${project.groupId} + blaze-persistence-integration-jaxrs-jackson-jakarta + ${project.version} + + + ${project.groupId} + blaze-persistence-integration-jaxrs-jsonb + ${project.version} + + + ${project.groupId} + blaze-persistence-integration-jaxrs-jsonb-jakarta ${project.version} @@ -530,6 +545,16 @@ blaze-persistence-integration-jackson-jakarta ${project.version} + + com.blazebit + blaze-persistence-integration-jsonb + ${project.version} + + + com.blazebit + blaze-persistence-integration-jsonb-jakarta + ${project.version} + com.blazebit blaze-persistence-integration-graphql @@ -540,6 +565,16 @@ blaze-persistence-integration-graphql-jakarta ${project.version} + + com.blazebit + blaze-persistence-integration-graphql-spqr + ${project.version} + + + com.blazebit + blaze-persistence-integration-graphql-spqr-jakarta + ${project.version} + com.blazebit blaze-persistence-integration-spring-hateoas-webmvc diff --git a/pom.xml b/pom.xml index 98247cecfa..4743fd25c7 100644 --- a/pom.xml +++ b/pom.xml @@ -324,6 +324,7 @@ **/src/main/java/com/blazebit/persistence/spring/data/webflux/impl/SortHandlerMethodArgumentResolver.java **/src/main/java/com/blazebit/persistence/spring/data/webflux/impl/SortArgumentResolver.java **/src/main/java/org/springframework/data/jpa/repository/query/FixedJpaQueryCreator.java + **/src/main/java/com/blazebit/persistence/integration/jsonb/jsonstructure/** **/src/main/java/com/blazebit/persistence/deltaspike/data/impl/meta/RepositoryDefinitionException.java **/src/main/java/com/blazebit/persistence/deltaspike/data/impl/meta/RepositoryComponents.java diff --git a/website/src/main/jbake/content/downloads.adoc b/website/src/main/jbake/content/downloads.adoc index 7408ad440b..9a677f9cbd 100644 --- a/website/src/main/jbake/content/downloads.adoc +++ b/website/src/main/jbake/content/downloads.adoc @@ -155,6 +155,18 @@ Older releases can be found on https://github.com/Blazebit/blaze-persistence/rel blaze-persistence-integration-jaxrs ${blaze-persistence.version} + + + com.blazebit + blaze-persistence-integration-jaxrs-jackson + ${blaze-persistence.version} + + + + com.blazebit + blaze-persistence-integration-jaxrs-jsonb + ${blaze-persistence.version} + @@ -170,6 +182,13 @@ Older releases can be found on https://github.com/Blazebit/blaze-persistence/rel ${blaze-persistence.version} + + + com.blazebit + blaze-persistence-integration-jsonb + ${blaze-persistence.version} + + com.blazebit