Entities are unmanaged when using Extended PersistentContext - jpa

Environment:
JDK 1.8
WildFly 10.0.0.Final
I have following #Stateful bean
#Stateful
#SessionScoped
#Local(CdiStatefulEmployeeService.class)
public class CdiStatefulEmployeeBean implements CdiStatefulEmployeeService {
#PersistenceContext(name = "employees", type = PersistenceContextType.EXTENDED)
EntityManager extendedEm;
private Employee cached;
#Override
public String service() {
cached = extendedEm.find(Employee.class, 499983);
return cached.getFirstName();
}
#Override
public String updateEntity() {
cached.setFirstName("Uri2");
//extendedEm.flush(); -- Line 1
return cached.getFirstName();
}
}
and following Servlet client
#WebServlet("/atInjectedStatefulEjbClient")
public class AtInjectedStatefulEjbClient extends HttpServlet {
#Inject
CdiStatefulEmployeeService statefulBean;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(true);
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
pw.println(statefulBean.service());
pw.println();
pw.println(statefulBean.updateEntity());
session.invalidate();
pw.flush();
pw.close();
}
}
Observation: Calling bean.updateEntity() method is not automatically saving the changes i.e. setting first name to "Uri2".
Question : Arent the entities managed across the calls in case of extended persistence context ?
Calling the flush() (i.e. uncommenting Line 1) has no effect either. Basically the entity is not managed in the updateEntity() call. I find this strange. Any thoughts ?
UPDATE:
Just to rule out the possibility, tried with same code but with following
No #SessionScoped
No #Inject (replaced with JDNI lookup inside servlet doGet())
Added #Remove method in the bean
Have servlet doGet() invoke #Remove once it is done with calling other 2 methods
Result : Same issue. Entity is not managed after first service call
Rakesh

Assuming that service method is called before updateEntity shows described behavior: Transaction attribute of service method is default, which means that, as you pointed out in your comment on updateEntity, the transaction is committed at end of service call. This commit makes the cached entity unmanaged. I suggest to annotate service method with #TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) which should keep cached entity managed and subsequent call of updateEntity finds managed entity.
Usually, with extended persistence contexts the facade EJB classes are annotated with #TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) and only EJB's methods that should commit get a different transaction attribute (see for example article Domain-driven design with Java EE 6).

Never ever inject STATEFUL (session dependent) beans into servlets (used by many users = by many sessions). If you use CMT the commit of the transaction will be executed after the call of the #Remove marked method. Where is it? It can be empty but it is critical at stateful beans.
But It would be a viable solution:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:inputText value="#{employeeMB.emplyeeName}"/>
<h:commandButton value="Submit" action="#{employeeMB.onSubmit}"/>
</h:form>
</h:body>
</html>
#ManagedBean
#SessionScoped
#Getter
#Setter
public class EmployeeMB
{
private String employeeName;
#EJB
private EmployeeSB employeeSB;
public void onSubmit()
{
employeeSB.persist( employeeName );
employeeName = "";
}
}
#Stateless
#LocalBean
public class EmployeeSB
{
#PersistenceContext
private EntityManager em;
public void persist( String employeeName_ )
{
Employee e = new Employee();
e.setName( employeeName_ );
em.persist( e );
}
}

Seems to be a bug in WildFly. All the documentation (Spec, books etc..) point out that Extended context meant to prevent entities from becoming detached when the transaction ends.
Filed Wildfly Issue : https://issues.jboss.org/browse/WFLY-6383

Related

JSF and synchronized lazyloading from EntityManager

I know EntityManager is not thread-safe but how to work with this in JSF?
Environment: Tomcat with Java-SE. Not Spring or something like that. Manual transaction management.
If I have an object like (pseudo code):
#Entity
class Person {
#OneToMany(lazy=true)
List<Address> addresses;
#ManyToOne(lazy=true)
Company company;
}
Then I have a NamedBean like
#Named
#SessionScoped
class PersonBean {
EntityManager entityManager;
public PersonBean(EntityManager em) { this.entityManager = em; }
public Person getPerson() {
em.find(Person.class, 1); // load the Person with id=1
}
}
And a page like
<html>
<body>
#{personBean.person.addresses[0].street}<br/>
#{personBean.person.addresses[0].number}<br/>
#{personBean.person.addresses[0].city}<br/>
#{personBean.person.company.address.street}<br/>
</body>
</html>
In this situation I can do the em.find() in a synchronized way to keep using the EntityManager in a thread safe manner. But what about the page itself? I assume JSF will load the values in parallel?
Any suggestions? Or is there no problem?
Thank you very much for any advice.

Is there a way to propagate SessionContext to a new thread (getting WELD-001303)?

there's a session scoped bean 'Identity' which I injected in a #Stateless bean which implements Runnable:
#Stateless
#LocalBean
public class Test implements Runnable {
#Inject
Identity identity;
#Inject
Logger log;
#Override
public void run() {
log.warn("Test: " + this + " " + identity.getAccount().getId());
}
}
There's also a bean which invokes the above Runnable asynchronously:
#Stateless
#LocalBean
public class BeanContextExecutor implements Executor {
#Asynchronous
#Override
public void execute(Runnable command) {
command.run();
}
}
and finally, the invocation looks like this:
#Stateless
public class OtherBean {
#Inject
BeanContextExecutor executor;
...
executor.execute(command);
...
}
When running this I'm getting the following error:
...
Caused by: org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
...
Is there any way to propagate the SessionContext to the background thread?
I also tried to submit this Runnable to ManagedExecutorService and even to create a proxy for it with a ContextService and submit a proxy but still getting the same error.
Thanks for any help with this!
As a workaround in BeanContextExecutor I used BoundSessionContext to create a dummy session context for a new thread and also had to manually copy the required session bean to make its state available in the background thread:
#Inject
BoundSessionContext boundSessionContext;
// Backed by a ConcurrentHashMap<Runnable, Identity> which stores the state of the session scoped bean before spawning a new thread
#Inject
GlobalExecutionContext globalExecutionContext;
#Inject
Instance<Identity> identityInstance;
#Inject
Cloner cloner;
#Inject
private BeanManager beanManager;
#Asynchronous
#Override
public void execute(Runnable command) {
HashMap<String, Object> storage = new HashMap<>();
boundSessionContext.associate(storage);
boundSessionContext.activate();
Identity identity = globalExecutionContext.remove(command);
Bean<Identity> bean = (Bean<Identity>) beanManager.resolve(beanManager.getBeans(Identity.class));
Identity localIdentity = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean));
cloner.copyPropertiesOfInheritedClass(identity, localIdentity);
command.run();
boundSessionContext.invalidate();
boundSessionContext.deactivate();
boundSessionContext.dissociate(storage);
}
The example is intended to demonstrate the approach, it's possible to improve it like support passing beans of an arbitrary type. But I don't like this approach at all. There should be a better solution for context propagation problem.
Update:
I'd like to keep the caller identity in a background thread even if initial session is expired, it looks like the above solution is suitable for this.

Extended Persistence Context with ViewScoped CDI beans

I am a long time Seam user who tries to move to Java EE7, JSF2.2 and CDI now.
In Seam you tend to use EntityManagers with extended scope most of the time (in the Seam Conversation Scope). You don't get any LIEs on Ajax request etc.
I am trying to do it in a similar way with Java EE7 and CDI but somehow the injected EntityManager is only transaction scoped. When I get a ajax request in the entities that were loaded before are not managed anymore.
I am using the new javax.faces.view.ViewScoped and javax.transactional.Transactional on my CDI bean.
My Producer:
#PersistenceContext(unitName = "primary", type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#Produces
#Default
#Dependent
public EntityManager getEntityManager() {
return entityManager;
}
And my CDI bean:
#Named
#ViewScoped
#Transactional
public class TestBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Inject
EntityManager entityManager;
Logger log = Logger.getLogger(TestBean.class);
private TestEntity lastTest = null;
public void testAdd(){
TestEntity test = new TestEntity();
test.setVal("Test "+System.currentTimeMillis());
entityManager.persist(test);
entityManager.flush();
log.infov("Created test entity {0}", test);
lastTest = test;
}
public void testRead(){
List<TestEntity> test = entityManager.createQuery("select t from TestEntity t").getResultList();
for(TestEntity t: test){
log.infov("Found {0} managed {1}",t,entityManager.contains(t));
}
if(lastTest!=null){
log.infov("Last Test {0} managed {1}",lastTest,entityManager.contains(lastTest));
}
}
So when I first call testAdd() via Ajax it creates a new test entity. When I then call testRead() it gets all test entities and checks that the last created test entity is still managed (which it should if it is an EntityManager with an extended persistent context). But entityManager.contains(lastTest) always returns false.
What am I doing wrong?
I believe I can't use #PersistenceContext directly in the CDI bean. So how do I get the desired (Seam like) behaviour?
When you specify that an injected EntityManager is an extended persistence context, all object instances remain managed. Extended persistence contexts can only be used within Stateful session beans.
This is according to JBOSS documentation: https://docs.jboss.org/ejb3/app-server/tutorial/extended_pc/extended.html.
Consider packaging your insert/update/delete operations into EJBs while simple read from database can be through CDI beans. But more complex operations involving multiple reads and writes as well as transaction should be within EJBs.

Jersey JAX-RS Glassfish 4 throwing java.lang.IllegalStateException

I am creating a simple RESTful service
#Path("/book")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Stateless
public class RestBookService {
#PersistenceContext(unitName="bookPU")
private EntityManager em;
#Context
protected UriInfo uriInfo;
#POST
public Response createBook(Book book) {
if (book == null)
throw new BadRequestException();
em.persist(book);
URI bookUri = uriInfo.getAbsolutePathBuilder().path(book.getId() + "").build();
return Response.created(bookUri).build();
}
}
The Book is simple JPA entity
#Entity
#XmlRootElement
public class Book {
static Logger log = Logger.getLogger(Book.class.getName());
public static final String FIND_ALL = "Book.find_all";
#Id
#GeneratedValue
private int id;
#Column(nullable=false)
private String title;
#Column
private Float price;
}
//Just giving a relevant code. There are getters/setters and the constructor
I am deploying the service using Maven on Glassfish 4.1
I am using
Jersey Container 2.13
Hibernate 4.3.5 Final
Mysql 5.1
and when I try to create a book using cURL as follows
curl -X POST --data-binary "<book><price>12.5</price><title>Book Title</title></book>" -H "Content-Type: application/xml" http://localhost:8080/book-service/rs/book -v
It is throwing following exception.
StandardWrapperValve[jersey-serlvet]: Servlet.service() for servlet jersey-serlvet threw exception
java.lang.IllegalStateException: Not inside a request scope.
at jersey.repackaged.com.google.common.base.Preconditions.checkState(Preconditions.java:149)
at org.glassfish.jersey.process.internal.RequestScope.current(RequestScope.java:228)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:156)
at org.jvnet.hk2.internal.MethodInterceptorImpl.invoke(MethodInterceptorImpl.java:74)
at org.jvnet.hk2.internal.MethodInterceptorInvocationHandler.invoke(MethodInterceptorInvocationHandler.java:62)
at com.sun.proxy.$Proxy239.getAbsolutePathBuilder(Unknown Source)
at com.services.bookrestservice.rest.RestBookService.createBook(RestBookService.java:44)
[There is another question similar to this but I have done exactly the same which is given in the answer still I am getting the exception. Also, I have gone through https://java.net/jira/browse/JERSEY-2241 but it seems to be in resolved state with the resolution as cannot reproduce. ]
Can somebody please help me.
EDIT1
I have changed from Stateless annotation to RequestScoped annotation as suggested by #HankCa. It is throwing following exception now.
'javax.persistence.TransactionRequiredException
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTxRequiredCheck(EntityManagerWrapper.java:161)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTransactionScopedTxCheck(EntityManagerWrapper.java:151)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.persist(EntityManagerWrapper.java:281)
at com.services.bookrestservice.rest.RestBookService.createBook(RestBookService.java:44)
'
Not sure why this exception because it is already in persistentcontext.
EDIT2
#HankCa suggested I did the following change.
Removed
#Context
protected UriInfo uriInfo;
And updated the method signature as
#POST
public Response createBook(Book book, #Context UriInfo uriInfo) {
And the service is working as expected. Thanks HankCa for your help.
Yes I stared at this one for far too long and my solution was as you found at Why is my Jersey JAX-RS server throwing a IllegalStateException about not being in RequestScope?. This was a year ago and I haven't hit it again (though I have been out of EJB land for a while) so I'll give it my best shot.
Specifically I would make these mods:
Add #RequestScoped
Put the #Context UriInfo uriInfo in the method or class. In the end i seemed to have gone in the method like:
This is code (and this is a line to separate the list from the code so the code shows as code!)
#Path("/user")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#RequestScoped
public class UserResource {
...
#PermitAll
#POST
public Response signupUser(CreateUserRequest request, #Context UriInfo uriInfo) {
AuthenticatedUserToken token = userService.createUser(request, Role.authenticated);
verificationTokenService.sendEmailRegistrationToken(token.getUserId());
URI location = uriInfo.getAbsolutePathBuilder().path(token.getUserId()).build();
return Response.created(location).entity(token).build();
}
I hope that helps!
Cheers,
bbos

#ManyToOne(fetch=FetchType.LAZY) lazy loading not working

I am working with JPA 2.1 (EclipseLink 2.5.1) and JBoss 7.1.
I've define very simple JPA entities:
#Entity
#Table(name="APLICACIONES_TB")
public class Aplicacion implements Serializable {
#Id
#Column(name="COD_APLICACION_V")
private long codAplicacionV;
#Column(name="APLICACION_V")
private String aplicacionV;
#OneToMany(mappedBy="aplicacion")
private Collection<Prestacion> prestaciones;
... getters and setters
}
#Entity
#Table(name="PRESTACIONES_TB")
public class Prestacion implements Serializable {
#Id
#Column(name="COD_PRESTACIONES_V")
private String codPrestacionesV;
#Column(name="DESCRIPCION_V")
private String descripcionV;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "COD_APLICACION_V")
private Aplicacion aplicacion;
... getters and setters ...
}
I have developed a staless EJB that executes a query to obtain some "Aplicacion" entities.
#Stateless
#LocalBean
public class DocuEJB implements DocuEJBLocal
{
#PersistenceContext(name="DocuEjb", type=PersistenceContextType.TRANSACTION)
private EntityManager em;
public Prestacion getResult(String name)
{
return em.createNamedQuery("ExampleQueryName", Prestacion.class).getSingleResult();
}
}
Because I'm working with JSF 2.1 the EJB is being injected in a managed bean:
#ManagedBean(name = "ManagedBean")
#RequestScoped
public class ManagedBean
{
#EJB DocuEJB docuEjb;
public String doSomething()
{
Prestacion entity = docuEjb.getResult("egesr");
if (entity != null)
{
// It should return null because 'entity' should be detached
Aplicacion app = entity.getAplicacion();
// but 'app' entity is not null, ¿why not?
System.out.println (app.getCodAplicacionV());
}
}
}
Lazy loading is not working even when lazy loading has been defined for 'aplicacion' field on 'Prestacion' entity. The code posted before should return a NullPointerException in the next line:
System.out.println (app.getCodAplicacionV());
because 'app' entity is detached and lazy loading has been configured.
Why is not working lazy loading?
Thanks
Try to add #Transactional on doSomething(), I think that your transaction manager is not well configured.
You can see here the official spring documentation. In any case, can you add your spring configurations, so that we can better help you. :)
I don't think the behavior your encounter is abnormal or your question should state it clearly:
EJB are by default transactional
Your JSF inject an EJB, with #EJB, and I guess JBoss can create a java reference and not a proxy
The entity is being managed because the transaction is not done, it will finish when doSomething ends.
Your entity is then loaded into the EntityManager, and lazy loading works because there is a context to it.
You would call em.evict(entity) with the result your are getting, this would probably fails because the entity would not be managed any more.