From e8a9f72206d22c2b034d86b50775818b19aeffe0 Mon Sep 17 00:00:00 2001 From: Zheng Feng Date: Tue, 8 Aug 2023 08:31:26 +0800 Subject: [PATCH] Revisit #4048 to Springless JPA extension (#5147) * Revert "Fixed JPA (CAMEL-19225)" This reverts commit 256e2f9db81c0c5007e21933a5927a1a832ba0c7. * Revisit #4048 to Springless JPA extension * Apply suggestions from code review Co-authored-by: James Netherton * Fix jpa docs * Refactor graal substitution --------- Co-authored-by: James Netherton --- .../ROOT/pages/reference/extensions/jpa.adoc | 29 ++++++++- extensions/jpa/deployment/pom.xml | 4 -- .../jpa/deployment/JpaProcessor.java | 23 ++++++++ extensions/jpa/runtime/pom.xml | 14 ++--- .../runtime/src/main/doc/configuration.adoc | 28 ++++++++- .../component/jpa/CamelJpaProducer.java | 31 ++++++++++ .../component/jpa/CamelJpaRecorder.java | 31 ++++++++++ .../jpa/QuarkusTransactionStrategy.java | 31 ++++++++++ .../component/jpa/graal/JpaSubstitution.java | 59 +++++++++++++++++++ .../orm/jpa/SharedEntityManagerCreator.java | 27 +++++++++ .../quarkus/component/jpa/it/JpaRoute.java | 9 ++- poms/bom/pom.xml | 4 ++ .../src/main/generated/flattened-full-pom.xml | 4 ++ .../main/generated/flattened-reduced-pom.xml | 19 ++---- .../flattened-reduced-verbose-pom.xml | 19 ++---- 15 files changed, 284 insertions(+), 48 deletions(-) create mode 100644 extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaProducer.java create mode 100644 extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaRecorder.java create mode 100644 extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/QuarkusTransactionStrategy.java create mode 100644 extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/graal/JpaSubstitution.java create mode 100644 extensions/jpa/runtime/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java diff --git a/docs/modules/ROOT/pages/reference/extensions/jpa.adoc b/docs/modules/ROOT/pages/reference/extensions/jpa.adoc index 0b55384217b4..f5af553fd8ef 100644 --- a/docs/modules/ROOT/pages/reference/extensions/jpa.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/jpa.adoc @@ -50,5 +50,32 @@ endif::[] The extension leverages https://quarkus.io/guides/hibernate-orm[Quarkus Hibernate ORM] to provide the JPA implementation via Hibernate. -Refer to the https://quarkus.io/guides/hibernate-orm[Quarkus Hibernate ORM] documentation to see how to configure Hibernate and your datasource, +Refer to the https://quarkus.io/guides/hibernate-orm[Quarkus Hibernate ORM] documentation to see how to configure Hibernate and your datasource. + +Also, it leverages https://quarkus.io/guides/transaction#programmatic-approach[Quarkus TX API] to provide `TransactionStrategy` implementation. + +When a single persistence unit is used, the Camel Quarkus JPA extension will automatically configure the JPA component with a +`EntityManagerFactory` and `TransactionStrategy`. + +[id="extensions-jpa-configuration-configuring-jpamessageidrepository"] +=== Configuring JpaMessageIdRepository +It needs to use `EntityManagerFactory` and `TransactionStrategy` from the CDI container to configure the `JpaMessageIdRepository`: +[source, java] +---- +@Inject +EntityManagerFactory entityManagerFactory; + +@Inject +TransactionStrategy transactionStrategy; + +from("direct:idempotent") + .idempotentConsumer( + header("messageId"), + new JpaMessageIdRepository(entityManagerFactory, transactionStrategy, "idempotentProcessor")); +---- + +[NOTE] +==== +Since it excludes the `spring-orm` dependency, some options such as `sharedEntityManager`, `transactionManager` are not supported. +==== diff --git a/extensions/jpa/deployment/pom.xml b/extensions/jpa/deployment/pom.xml index 3738c46d17e8..445540193ce2 100644 --- a/extensions/jpa/deployment/pom.xml +++ b/extensions/jpa/deployment/pom.xml @@ -38,10 +38,6 @@ org.apache.camel.quarkus camel-quarkus-core-deployment - - org.apache.camel.quarkus - camel-quarkus-support-spring-deployment - org.apache.camel.quarkus camel-quarkus-jpa diff --git a/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java b/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java index 6d5364145680..adfc8405b81e 100644 --- a/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java +++ b/extensions/jpa/deployment/src/main/java/org/apache/camel/quarkus/component/jpa/deployment/JpaProcessor.java @@ -16,8 +16,16 @@ */ package org.apache.camel.quarkus.component.jpa.deployment; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.FeatureBuildItem; +import org.apache.camel.component.jpa.JpaComponent; +import org.apache.camel.quarkus.component.jpa.CamelJpaProducer; +import org.apache.camel.quarkus.component.jpa.CamelJpaRecorder; +import org.apache.camel.quarkus.core.deployment.spi.CamelRuntimeBeanBuildItem; class JpaProcessor { @@ -27,4 +35,19 @@ class JpaProcessor { FeatureBuildItem feature() { return new FeatureBuildItem(FEATURE); } + + @Record(ExecutionTime.RUNTIME_INIT) + @BuildStep + void configureJpaComponentBean( + BuildProducer additionalBeans, + BuildProducer camelRuntimeBean, + CamelJpaRecorder recorder) { + additionalBeans.produce(new AdditionalBeanBuildItem(CamelJpaProducer.class)); + + camelRuntimeBean.produce( + new CamelRuntimeBeanBuildItem( + "jpa", + JpaComponent.class.getName(), + recorder.createJpaComponent())); + } } diff --git a/extensions/jpa/runtime/pom.xml b/extensions/jpa/runtime/pom.xml index 1a8c05a9f790..7cb1dc7a3310 100644 --- a/extensions/jpa/runtime/pom.xml +++ b/extensions/jpa/runtime/pom.xml @@ -36,6 +36,11 @@ + + org.graalvm.sdk + graal-sdk + provided + io.quarkus quarkus-hibernate-orm @@ -44,19 +49,10 @@ org.apache.camel.quarkus camel-quarkus-core - - org.apache.camel.quarkus - camel-quarkus-support-spring - org.apache.camel camel-jpa - - - org.springframework - spring-tx - diff --git a/extensions/jpa/runtime/src/main/doc/configuration.adoc b/extensions/jpa/runtime/src/main/doc/configuration.adoc index f398236b25fa..0be017ccf153 100644 --- a/extensions/jpa/runtime/src/main/doc/configuration.adoc +++ b/extensions/jpa/runtime/src/main/doc/configuration.adoc @@ -1,3 +1,29 @@ The extension leverages https://quarkus.io/guides/hibernate-orm[Quarkus Hibernate ORM] to provide the JPA implementation via Hibernate. -Refer to the https://quarkus.io/guides/hibernate-orm[Quarkus Hibernate ORM] documentation to see how to configure Hibernate and your datasource, +Refer to the https://quarkus.io/guides/hibernate-orm[Quarkus Hibernate ORM] documentation to see how to configure Hibernate and your datasource. + +Also, it leverages https://quarkus.io/guides/transaction#programmatic-approach[Quarkus TX API] to provide `TransactionStrategy` implementation. + +When a single persistence unit is used, the Camel Quarkus JPA extension will automatically configure the JPA component with a +`EntityManagerFactory` and `TransactionStrategy`. + +=== Configuring JpaMessageIdRepository +It needs to use `EntityManagerFactory` and `TransactionStrategy` from the CDI container to configure the `JpaMessageIdRepository`: +[source, java] +---- +@Inject +EntityManagerFactory entityManagerFactory; + +@Inject +TransactionStrategy transactionStrategy; + +from("direct:idempotent") + .idempotentConsumer( + header("messageId"), + new JpaMessageIdRepository(entityManagerFactory, transactionStrategy, "idempotentProcessor")); +---- + +[NOTE] +==== +Since it excludes the `spring-orm` dependency, some options such as `sharedEntityManager`, `transactionManager` are not supported. +==== diff --git a/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaProducer.java b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaProducer.java new file mode 100644 index 000000000000..25ffd6e2c7b0 --- /dev/null +++ b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaProducer.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.quarkus.component.jpa; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import org.apache.camel.component.jpa.TransactionStrategy; + +@Dependent +public class CamelJpaProducer { + @Produces + @ApplicationScoped + public TransactionStrategy quarkusTransactionStrategy() { + return new QuarkusTransactionStrategy(); + } +} diff --git a/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaRecorder.java b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaRecorder.java new file mode 100644 index 000000000000..658d5424ee06 --- /dev/null +++ b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/CamelJpaRecorder.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.quarkus.component.jpa; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; +import org.apache.camel.component.jpa.JpaComponent; + +@Recorder +public class CamelJpaRecorder { + + public RuntimeValue createJpaComponent() { + JpaComponent component = new JpaComponent(); + component.setTransactionStrategy(new QuarkusTransactionStrategy()); + return new RuntimeValue<>(component); + } +} diff --git a/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/QuarkusTransactionStrategy.java b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/QuarkusTransactionStrategy.java new file mode 100644 index 000000000000..755968491acc --- /dev/null +++ b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/QuarkusTransactionStrategy.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.quarkus.component.jpa; + +import io.quarkus.narayana.jta.QuarkusTransaction; +import io.quarkus.narayana.jta.RunOptions; +import org.apache.camel.component.jpa.TransactionStrategy; + +import static io.quarkus.narayana.jta.QuarkusTransaction.runOptions; + +public class QuarkusTransactionStrategy implements TransactionStrategy { + @Override + public void executeInTransaction(Runnable runnable) { + QuarkusTransaction.run(runOptions().semantic(RunOptions.Semantic.JOIN_EXISTING), runnable); + } +} diff --git a/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/graal/JpaSubstitution.java b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/graal/JpaSubstitution.java new file mode 100644 index 000000000000..b7672a1fa985 --- /dev/null +++ b/extensions/jpa/runtime/src/main/java/org/apache/camel/quarkus/component/jpa/graal/JpaSubstitution.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.quarkus.component.jpa.graal; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Delete; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import jakarta.persistence.EntityManagerFactory; +import org.apache.camel.component.jpa.DefaultTransactionStrategy; +import org.apache.camel.component.jpa.JpaComponent; +import org.apache.camel.component.jpa.JpaEndpoint; +import org.apache.camel.component.jpa.TransactionStrategy; + +final public class JpaSubstitution { +} + +@TargetClass(JpaEndpoint.class) +final class JpaEndpointSubstitution { + @Alias + private TransactionStrategy transactionStrategy; + + @Substitute + protected EntityManagerFactory createEntityManagerFactory() { + throw new UnsupportedOperationException("createEntityManagerFactory is not supported"); + } + + @Substitute + public TransactionStrategy getTransactionStrategy() { + return transactionStrategy; + } +} + +@TargetClass(JpaComponent.class) +final class JpaComponentSubstitution { + @Substitute + private void createTransactionStrategy() { + } +} + +@TargetClass(DefaultTransactionStrategy.class) +@Delete +final class DefaultTransactionStrategySubstitution { +} diff --git a/extensions/jpa/runtime/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java b/extensions/jpa/runtime/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java new file mode 100644 index 000000000000..07f90799be99 --- /dev/null +++ b/extensions/jpa/runtime/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.springframework.orm.jpa; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; + +public abstract class SharedEntityManagerCreator { + public static EntityManager createSharedEntityManager(EntityManagerFactory emf) { + throw new UnsupportedOperationException("createSharedEntityManager is not supported"); + } +} diff --git a/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java b/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java index 6f3bf380f28c..737dcd7bd101 100644 --- a/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java +++ b/integration-tests/jpa/src/main/java/org/apache/camel/quarkus/component/jpa/it/JpaRoute.java @@ -22,16 +22,19 @@ import jakarta.inject.Inject; import jakarta.persistence.EntityManagerFactory; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.jpa.TransactionStrategy; +import org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository; import org.apache.camel.quarkus.component.jpa.it.model.Fruit; -import static org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository.jpaMessageIdRepository; - @ApplicationScoped public class JpaRoute extends RouteBuilder { @Inject EntityManagerFactory entityManagerFactory; + @Inject + TransactionStrategy transactionStrategy; + @Override public void configure() throws Exception { String jpaEndpoint = "jpa:" + Fruit.class.getName(); @@ -72,7 +75,7 @@ public void configure() throws Exception { from("direct:idempotent") .idempotentConsumer( header("messageId"), - jpaMessageIdRepository(entityManagerFactory, "idempotentProcessor")) + new JpaMessageIdRepository(entityManagerFactory, transactionStrategy, "idempotentProcessor")) .log("Consumes messageId: ${header.messageId}") .to("mock:idempotent"); diff --git a/poms/bom/pom.xml b/poms/bom/pom.xml index 31b2b85bf441..4bab97564451 100644 --- a/poms/bom/pom.xml +++ b/poms/bom/pom.xml @@ -1582,6 +1582,10 @@ org.apache.camel camel-spring + + org.springframework + spring-orm + diff --git a/poms/bom/src/main/generated/flattened-full-pom.xml b/poms/bom/src/main/generated/flattened-full-pom.xml index 42fdac7670a0..46a36772434e 100644 --- a/poms/bom/src/main/generated/flattened-full-pom.xml +++ b/poms/bom/src/main/generated/flattened-full-pom.xml @@ -1520,6 +1520,10 @@ org.apache.camel camel-spring + + org.springframework + spring-orm + diff --git a/poms/bom/src/main/generated/flattened-reduced-pom.xml b/poms/bom/src/main/generated/flattened-reduced-pom.xml index 5f6dbea01483..4b4f99d8367c 100644 --- a/poms/bom/src/main/generated/flattened-reduced-pom.xml +++ b/poms/bom/src/main/generated/flattened-reduced-pom.xml @@ -1520,6 +1520,10 @@ org.apache.camel camel-spring + + org.springframework + spring-orm + @@ -6578,21 +6582,6 @@ - - org.springframework - spring-orm - 6.0.11 - - - org.springframework - spring-beans - - - org.springframework - spring-core - - - org.springframework spring-tx diff --git a/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml b/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml index c0d120cc8a80..38b25399f0c3 100644 --- a/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml +++ b/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml @@ -1520,6 +1520,10 @@ org.apache.camel camel-spring + + org.springframework + spring-orm + @@ -6578,21 +6582,6 @@ - - org.springframework - spring-orm - 6.0.11 - - - org.springframework - spring-beans - - - org.springframework - spring-core - - - org.springframework spring-tx