Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure the entity manager works when no request scope is present #868

Merged
merged 1 commit into from
Feb 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
import org.jboss.shamrock.jpa.runtime.DefaultEntityManagerProducer;
import org.jboss.shamrock.jpa.runtime.JPAConfig;
import org.jboss.shamrock.jpa.runtime.JPADeploymentTemplate;
import org.jboss.shamrock.jpa.runtime.RequestScopedEntityManagerHolder;
import org.jboss.shamrock.jpa.runtime.TransactionEntityManagers;
import org.jboss.shamrock.jpa.runtime.boot.scan.ShamrockScanner;

Expand Down Expand Up @@ -134,7 +135,7 @@ void handleNativeImageImportSql(BuildProducer<SubstrateResourceBuildItem> resour

@BuildStep
void registerBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans, CombinedIndexBuildItem combinedIndex, List<PersistenceUnitDescriptorBuildItem> descriptors) {
additionalBeans.produce(new AdditionalBeanBuildItem(false, JPAConfig.class, TransactionEntityManagers.class));
additionalBeans.produce(new AdditionalBeanBuildItem(false, JPAConfig.class, TransactionEntityManagers.class, RequestScopedEntityManagerHolder.class));

if (descriptors.size() == 1) {
// There is only one persistence unit - register CDI beans for EM and EMF if no
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* 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 org.jboss.shamrock.jpa.runtime;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;


/**
* Bean that is used to manage request scoped entity managers
*/
@RequestScoped
public class RequestScopedEntityManagerHolder {

private final Map<String, EntityManager> entityManagers = new HashMap<>();

public EntityManager getOrCreateEntityManager(String name, EntityManagerFactory factory) {
return entityManagers.computeIfAbsent(name, (n) -> factory.createEntityManager());
}

@PreDestroy
public void destroy() {
for (Map.Entry<String, EntityManager> entry : entityManagers.entrySet()) {
entry.getValue().close();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,14 @@
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;

import org.jboss.shamrock.jpa.runtime.entitymanager.TransactionScopedEntityManager;

@RequestScoped
public class TransactionEntityManagers {

@Inject
Expand All @@ -40,6 +38,9 @@ public class TransactionEntityManagers {
@Inject
JPAConfig jpaConfig;

@Inject
Instance<RequestScopedEntityManagerHolder> requestScopedEntityManagers;

private final Map<String, TransactionScopedEntityManager> managers;

public TransactionEntityManagers() {
Expand All @@ -48,14 +49,7 @@ public TransactionEntityManagers() {

public EntityManager getEntityManager(String unitName) {
return managers.computeIfAbsent(unitName,
un -> new TransactionScopedEntityManager(tm, tsr, jpaConfig.getEntityManagerFactory(un)));
}

@PreDestroy
public void destroy() {
for (TransactionScopedEntityManager manager : managers.values()) {
manager.requestDone();
}
un -> new TransactionScopedEntityManager(tm, tsr, jpaConfig.getEntityManagerFactory(un), unitName, requestScopedEntityManagers));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import java.util.Map;

import javax.enterprise.inject.Instance;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
Expand All @@ -38,24 +39,26 @@
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;

import org.jboss.shamrock.jpa.runtime.RequestScopedEntityManagerHolder;

public class TransactionScopedEntityManager implements EntityManager {

private final TransactionManager transactionManager;
private final TransactionSynchronizationRegistry tsr;
private final EntityManagerFactory emf;
private final String unitName;
private static final Object transactionKey = new Object();
private EntityManager fallbackEntityManager;
private final Instance<RequestScopedEntityManagerHolder> requestScopedEms;

public TransactionScopedEntityManager(TransactionManager transactionManager, TransactionSynchronizationRegistry tsr, EntityManagerFactory emf) {
public TransactionScopedEntityManager(TransactionManager transactionManager,
TransactionSynchronizationRegistry tsr,
EntityManagerFactory emf,
String unitName, Instance<RequestScopedEntityManagerHolder> requestScopedEms) {
this.transactionManager = transactionManager;
this.tsr = tsr;
this.emf = emf;
}

public void requestDone() {
if(fallbackEntityManager != null) {
fallbackEntityManager.close();
}
this.unitName = unitName;
this.requestScopedEms = requestScopedEms;
}

EntityManagerResult getEntityManager() {
Expand All @@ -81,10 +84,11 @@ public void afterCompletion(int i) {
});
return new EntityManagerResult(newEm, false);
} else {
if(fallbackEntityManager == null) {
fallbackEntityManager = emf.createEntityManager();
}
return new EntityManagerResult(emf.createEntityManager(), false);
//this will throw an exception if the request scope is not active
//this is expected as either the request scope or an active transaction
//is required to properly managed the EM lifecycle
RequestScopedEntityManagerHolder requestScopedEms = this.requestScopedEms.get();
return new EntityManagerResult(requestScopedEms.getOrCreateEntityManager(unitName, emf), false);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.shamrock.jpa.tests.configurationless;

import java.util.HashMap;
import java.util.Map;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
Expand All @@ -9,8 +12,6 @@
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Map;

/**
* @author Emmanuel Bernard emmanuel@hibernate.org
Expand Down Expand Up @@ -38,8 +39,7 @@ public Map<String, String> create() {
em.clear();
gift = em.find(Gift.class, gift.getId());
map.put("jpa", "Roller coaster".equals(gift.getName()) ? "OK" : "Boooo");
}
catch (Exception e) {
} catch (Exception e) {
map.put("exception message", e.getMessage());
}
return map;
Expand All @@ -60,13 +60,11 @@ public Map<String, String> createUserTransaction() {
gift = em.find(Gift.class, gift.getId());
transaction.commit();
map.put("jpa", "Roller coaster".equals(gift.getName()) ? "OK" : "Boooo");
}
catch (Exception e) {
} catch (Exception e) {
map.put("exception message", e.getMessage());
try {
transaction.rollback();
}
catch (Exception ne) {
} catch (Exception ne) {
//swallow the bastard
}
}
Expand All @@ -85,4 +83,12 @@ public Map<String, String> get() {
return map;
}

@GET
@Produces(MediaType.TEXT_PLAIN)
@Transactional
@Path("/cake")
public String getCake() {
Cake c = (Cake) em.createQuery("from Cake").getSingleResult();
return c.getType();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.shamrock.jpa.tests.configurationless;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(uniqueConstraints = @UniqueConstraint(name = "typeConstraint", columnNames = "type"))

public class Cake {
private Long id;
private String type;

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="cakeSeq")
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}


public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.shamrock.jpa.tests.configurationless;

import javax.enterprise.context.Dependent;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.jboss.shamrock.runtime.StartupEvent;

/**
* creates a chocolate cake on startup to make sure JPA works in the startup event
*/
@Dependent
public class StartupCakeManager {

@Inject
EntityManager entityManager;

@Transactional
public void startup(@Observes StartupEvent startupEvent) {
Cake c = new Cake();
c.setType("Chocolate");
entityManager.persist(c);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.shamrock.jpa.tests.configurationless;

import static org.hamcrest.core.StringContains.containsString;

import org.jboss.shamrock.test.junit.ShamrockTest;
import org.junit.jupiter.api.Test;

import io.restassured.RestAssured;

/**
* @author Emmanuel Bernard emmanuel@hibernate.org
*/
@ShamrockTest
public class JPAOnStartupTest {

@Test
public void testStartupJpa() {
RestAssured.when().get("/jpa-test/cake").then()
.body(containsString("Chocolate"));
}
}