Skip to content

Commit

Permalink
Revisit #4048 to Springless JPA extension (#5147)
Browse files Browse the repository at this point in the history
* Revert "Fixed JPA (CAMEL-19225)"

This reverts commit 256e2f9.

* Revisit #4048 to Springless JPA extension

* Apply suggestions from code review

Co-authored-by: James Netherton <[email protected]>

* Fix jpa docs

* Refactor graal substitution

---------

Co-authored-by: James Netherton <[email protected]>
  • Loading branch information
zhfeng and jamesnetherton committed Aug 14, 2023
1 parent ea020ce commit e8a9f72
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 48 deletions.
29 changes: 28 additions & 1 deletion docs/modules/ROOT/pages/reference/extensions/jpa.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
====

4 changes: 0 additions & 4 deletions extensions/jpa/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-support-spring-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-jpa</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -27,4 +35,19 @@ class JpaProcessor {
FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
void configureJpaComponentBean(
BuildProducer<AdditionalBeanBuildItem> additionalBeans,
BuildProducer<CamelRuntimeBeanBuildItem> camelRuntimeBean,
CamelJpaRecorder recorder) {
additionalBeans.produce(new AdditionalBeanBuildItem(CamelJpaProducer.class));

camelRuntimeBean.produce(
new CamelRuntimeBeanBuildItem(
"jpa",
JpaComponent.class.getName(),
recorder.createJpaComponent()));
}
}
14 changes: 5 additions & 9 deletions extensions/jpa/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
</properties>

<dependencies>
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
Expand All @@ -44,19 +49,10 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-support-spring</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jpa</artifactId>
</dependency>

<dependency><!-- https://github.com/apache/camel-quarkus/issues/4084 -->
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
28 changes: 27 additions & 1 deletion extensions/jpa/runtime/src/main/doc/configuration.adoc
Original file line number Diff line number Diff line change
@@ -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.
====
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<JpaComponent> createJpaComponent() {
JpaComponent component = new JpaComponent();
component.setTransactionStrategy(new QuarkusTransactionStrategy());
return new RuntimeValue<>(component);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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");

Expand Down
4 changes: 4 additions & 0 deletions poms/bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,10 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
Expand Down
4 changes: 4 additions & 0 deletions poms/bom/src/main/generated/flattened-full-pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1520,6 +1520,10 @@
<groupId>org.apache.camel</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
<artifactId>camel-spring</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
</exclusion>
<exclusion>
<groupId>org.springframework</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
<artifactId>spring-orm</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->
</exclusion>
</exclusions>
</dependency>
<dependency>
Expand Down
Loading

0 comments on commit e8a9f72

Please sign in to comment.