Injected EntityManager's scope/lifecycle in a Stateless bean - java

Can someone explain the lifecycle of an injected EntityManager in a stateless bean? If a stateless bean has an injected EntityManager associated with a specific PersistenceContext, what happens to that association the second time the bean is used?
For example, I have the following:
#Stateless
public class TimeStepsBean
{
#PersistenceContext(unitName="DynamicDB")
private EntityManager em;
public List<TimeStep> timeSteps = new ArrayList<TimeStep>();
private void init()
{
if (timeSteps.isEmpty())
{
TypedQuery<TimeStep> query = em.createQuery("SELECT t FROM TimeStep t", TimeStep.class);
timeSteps = query.getResultList();.
}
}
public void refreshSteps()
{
init();
em.flush();
em.refresh(timeSteps.get(0));
}
}
When refreshSteps is called the second time the bean is used (a second transaction), I get a "java.lang.IllegalArgumentException: Entity not managed". The entityManager was injected, so I am assuming that it is always part of the current persistence context. Is that true?
Adding em.merge(timeSteps.get(0)) before the refresh still generates the same exception.

Your timeSteps is a state. Your class is annotated as stateless. It's a misuse of the framework.

Related

How to pass a JPA entity to an Asynchronous EJB method

While processing a request, I would like to 'kick off' separate task in a separate transaction based on the data received and stored in the database.
The main advantage is that I can return the result to my request before all additional processing is done, also if additional processing fails, this will not affect the original processing of the request (as they are in different transactions). This additional processing might require adding extra information to the data.
I have the following setup in mind. Where using #Asynchronous the additional task is created.
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
}
}
#Stateless
public class Bean2
{
#Asynchronous // This causes a new transaction to happen
public void asyncActOnData(MyInput input){
// Act upon data and store result in DB
}
}
This seems like a clean way, however this causes JPA Entity to become detached, possibly during processing in Bean2.
Currently I don't plan on changing the data after the persist call (and asnyc), but as the application grows I feel it would be safer to allow this to avoid mistakes.
What is the correct way to kick off the separate asynchronous task based on the persisted data?
I am using: Java EE 6 with Eclipselink 2.5.2 for JPA.
You can continue to make use of the detached instance provided that:
You're not planning to perform further persistence operations;
All associated entities (if any) were available when asyncActOnData was invoked.
However, if you need to perform further persistence operations you can do the following:
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
// Flush the persistence context and detach the entity
// An entity is not permitted to be in more than one
// persistence context at a time. This should prevent
// a race condition with the merge in bean2.
entityManager.flush();
entityManager.detach(myEntity);
Future<Result> futureResult = bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
....
return futureResult.get();
}
}
#Stateless
public class Bean2 {
#PersistenceContext
private EntityManager entityManager;
#Asynchronous
public Future<Result> asyncActOnData(MyInput input) {
// this will ensure that the database still matches input
// and add input into the persistence context
input = entityManager.merge(input);
...
return new javax.ejb.AsyncResult<Result>(result);
}
}
You will find it useful to read §3.2 of the "Java™ Persistence API, Version 2.1" specification.

#Remote EJB which uses #RequestScoped CDI bean

I have an application which has a #Remote #Singleton EJB which injects a #RequestScoped entity manager produced by CDI. Another application on the same server (wildfly 9)/JVM will use this EJB to get a result fetched from the entity manager.
The first invocation of the EJB will return the expected result. It produces the entity manager, fetches the data and disposes the entity manager again when the invocation returns. Each subsequent invocation of that EJB will throw an error because of a closed entity manager. No produce / dispose for a new entity manager is made.
Is this the expected bahavior? Do I have an error in my code?
IFrameworkResourceManager framework = _applicationContext.getFrameworkResourceManager();
final User resolvedUser = framework.resolveUser(username, domain);
// ...
final Rights resolvedRights = framework.resolveRights(resolvedUser.getGuid(), applicationId);
// ...
This piece of code is executed in a CDI producer which again is executed as soon as a new http session is created for an user. Nothing changes if I call getFramworkResourceManager again before invoking resolveRights.
public IFrameworkResourceManager getFrameworkResourceManager() {
return IFrameworkResourceManager frm = (IFrameworkResourceManager) ctx
.lookup("java:global/WebFramework/WebFrameworkImpl!my.package.IWebFramework");
}
It doesn't matter if I use a direct JNDI lookup or #EJB injection. The returned instance is reported (toString()) as Proxy for remote EJB StatelessEJBLocator for "/WebFramework/WebFrameworkImpl", view is interface my.package.IWebFramework, affinity is None
#LocalBean
#Singleton
public class WebFrameworkImpl implements IWebFramework, Serializable {
#Inject
private EntityManager _entityManager;
#Override
public User resolveUser(String username, String domain) {
System.out.println(_entityManager + " || " + _entityManager.isOpen());
// execute query using QueryDSL and the injected entityManager
}
#Override
public Rights resolveRights(String guidUser, int applicationId) {
System.out.println(_entityManager + " || " + _entityManager.isOpen());
// execute query using QueryDSL and the injected entityManager
}
}
#Remote
public interface IWebFramework extends IFrameworkResourceManager {
// some methods...
}
public interface IFrameworkResourceManager {
public User resolveUser(String username, String domain);
public Rights resolveRights(String guidUser, int applicationId);
}
Sysout of resolveUser: org.hibernate.jpa.internal.EntityManagerImpl#379e882b || true
Sysout of resolveRights: org.hibernate.jpa.internal.EntityManagerImpl#379e882b || false
Edit 20.11.2015 13:43: Persistence unit is of type RESOURCE_LOCAL. Additionally all #ResourceScoped beans are affected. #PostConstruct and #PreDestroy are only invoked for the first EJB invocation. Each subsequent invocation uses the previous instance of the resource scoped bean which is not correct.
Edit 20.11.2015 13:55: Everything works as expected if the EJB is invoked from within the same application that provides the EJB. This behavior only appears for invocations from other applications.
Edit 20.11.2015 15:24: JBoss AS 7.1.3.Final, Wildfly 9.0.0.Final and Wildfly 10.0.0.CR4 are all effected. But according to the CDI spec (1.0 to 1.2) chapter 6.7.4 this should work. I've filled a bug report (WFLY-5716).
When using RESOURCE_LOCAL, you shoud create youe EntityManager from EntityManagerFacgtory and handle it by yourself, like:
private EntityManagerFactory factory = Persistence.createEntityManagerFactory("unit-name");
public void someMethod(){
EntityManager em = emf.createEntityManager();
EntityTransaction tx = null;
try {
tx = em.getTransaction();
tx.begin();
// do some work
tx.commit();
}
catch (RuntimeException e) {
if ( tx != null && tx.isActive() )
tx.rollback();
throw e; // or display error message
}
finally {
em.close();
}
}
Bugfix for this wired behavior is already merged in the WELD repository:
WFLY-5716
WELD-2069

Pass Injected EntityManager to a web session persisted object

Is it secure to pass a Injected EntityManager created on an EJB, to a method that will return an Object, and after, persist that Object on a Web Session for web clients use it?
Like in this example:
the EJB
#Stateless(mappedName = "MyService")
#LocalBean
public class MyService implements MyServiceLocal {
#PersistenceContext(unitName="primary")
private EntityManager em;
/**
* Default constructor.
*/
public MyService() {
}
#Override
public Service newServiceX(User user) {
return new ServiceX(user,em); // here, passing the EntityManager
}
}
After, I persist this Service in a web client (using struts):
The base action
public class YAction extends ActionSupport implements SessionAware{
#Inject
private MyServiceLocal service;
public String execute(){
Service x = service.newServiceX();
persistInCookie("ServiceX",x);
}
public void persistInCookie(String, Object){
// persist
}
}
And after, using another Action:
// another Action that
class XAction{
public String useService(){
getService().doSomething();
}
protected Service getService(){
Service service = (Service) getSessionMap().get("ServiceX");
return service;
}
}
the POJO class ServiceX using the EntityManager:
public class ServiceX extends Service{
EntityManager em;
public ServiceX(User user, EntityManager em){
this.em = em;
}
public void doSomething(){
// do something with the EntityManager passed by the EJB
}
}
First, the action that would be call is the Y action to persist the Service on the Session, next, the X action will return the Service persisted on the Session and try to use it.
I believe that the EJB Stateless Session Bean can close My EntityManager and this ServiceX POJO class can't use it. This can happen? I found similar question HERE, but in this question, the EntityManager is passed to a helper class. In my case is different because I want to persist this Object on a session cookie, and use later.
I don't think It is a good idea to store a EntityManager in SessionMap. What is more, I don't even think that it is a good idea to perform EntityManager operations outside the EJB container.
Have read about transaction-boundaries in JPA?
By default, EJB container is using CMT (Container Managed Transactions). In this case, container uses entitymanager-per-request pattern which means that the transaction begins and ends when one of the business methods of MyService starts and ends (transaction is committed or rollbacked in case of RuntimeException). For whole transaction time, EntityManager is connected with the same PersistenceContext. After the transaction is ended the container closes EntityManager which means that the EntityManager is disconnected with recent PersistenceContext:
// transaction begins
Service x = service.newServiceX();
// transaction ends
This might be crucial if you were going to do some update/insert operations outside the transaction.
Now, when you call EntityManager operation (like find) outside the transaction, for every each operation the EntityManager will create new PersistentContext. This may cause some issues, as two entities that represent the same record will be treated as different entities:
// each operation occurs in a separate persistence context, and returns
// a new detached instance
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 != mag1);
Some more articles to read:
Persistent Context
Transactions and Concurrency
Entity Lifecycle Management

Statefull Ejb and Session scoped Managed bean Inject null pointer

Could anyone help me? There is a SessionScoped Managed Bean and one Stateless Ejb and one other Stateful Ejb..
The serachCustomer() method in the MB call the injected DaoEjbTst searcCustomer() method which return with an instance of BCus entity object. I injected in to Stateless DaoEjbTst Ejb the other Stateful CustomerSession EJB and when the entity instance is ready in the DaoEjbTst EJB i call the CustomerSession EJB setActualCustomer method and i give as paramter the entity instance for this method and try to store it... Then when i try to get this "stored" entity instance with another showTstDate() method in the ManagedBean it's throws NullPointer Exception.. And i dont know why.. Why doesn't exist the public BCus actualCustomer paramter in the stateful ejb? I tryed to create #PreDestroy and #PrePassivate and #Remove methods in the Stateful Ejb to check if the container remove it but this methods never invoked by the container.. So i'm sure the ejb exist.. but inspite of this i can't access it :( I don't use interfaces.
Here is my managedbean:
#EJB
private DaoEjbTst daoEjb;
#EJB
private CustomerSession customerSession;
public void serachCustomer() throws IOException {
FacesContext ctx = FacesContext.getCurrentInstance();
if (daoEjb.searcCustomer(custNo)) {
ctx.getExternalContext().redirect("showCustomer.xhtml");
}
else {
ctx.getExternalContext().redirect("test.xhtml");
}
}
public String showTstDate() {
log.info("MB EJB EXIST: " + customerSession);
return "Test: " + customerSession.getActualCustomer().getCustName();
}
Here is my DaoEjbTst:
#Stateless
public class DaoEjbTst {
private final Logger log = Logger.getLogger("DaoEjbTst.class");
#EJB
private CustomerSession customerSession;
public CustomerSession getCustomerSession() {
return customerSession;
}
public void setCustomerSession(CustomerSession customerSession) {
this.customerSession = customerSession;
}
#PersistenceContext(unitName = "TestAppPU")
private EntityManager em;
public boolean searcCustomer(String custNo) {
try {
BCus cus = (BCus) em.createNamedQuery("BCus.findByCustomerno").setParameter("customerno", custNo).getSingleResult();
log.info("DAOEJB: " + cus);
customerSession.setActualCustomer(cus);
return true;
}
catch (NoResultException e) {
log.info(e.getMessage());
return false;
}
}
And here is my CustomerSession EJb:
#Stateful
public class CustomerSession {
public BCus actualCustomer;
private final Logger log = Logger.getLogger("CustomerSession.class");
public BCus getActualCustomer() {
return actualCustomer;
}
public void setActualCustomer(BCus actualCustomer) {
this.actualCustomer = actualCustomer;
checkTst();
}
public CustomerSession() {
}
}
I think that CustomerSession bean injected in ManagedBean isn't the same bean instance that is injected in DaoEjbTst. So invoking:
customerSession.getActualCustomer()
in ManagedBean simply returns null because ActualCustomer field was not set for this particular bean instance. It was set in DaoEjbTest but this is different instance of CustomerSession. So:
DaoEjbTst.getCustomerSession().equals(ManagedBean.getCustomerSession())
gives false.
When you look into specification EJB 3.1 (section 3.4.7.1) you see:
A stateful session object has a unique identity that is assigned by the container at the time the object is
created.
Basically what you should do is to setActualCustomer for the instance of CustomerSession bean in ManagedBean with value that is found in DAO's searcCustomer() method. However storing stateful session bean inside stateless bean is a very bad idea and I suggest you to rethink your architecture.

Please explain the #Produces annotation in CDI

I have read about the #Produces annotation in CDI, but I don't understand its usage.
public class Resources {
// Expose an entity manager using the resource producer pattern
#SuppressWarnings("unused")
#PersistenceContext
#Produces
private EntityManager em; //
#Produces
Logger getLogger(InjectionPoint ip) { //
String category = ip.getMember()
.getDeclaringClass()
.getName();
return Logger.getLogger(category);
}
#Produces
FacesContext getFacesContext() { //
return FacesContext.getCurrentInstance();
}
}
taken from: http://www.jboss.org/jdf/quickstarts/jboss-as-quickstart/guide/GreeterQuickstart/#GreeterQuickstart-
How does the container know to call a producer method? If I inject an EntityManager, how does the container call the #produces EntityManager? And how would a getLogger producer method get called?
I also don't see the reason to go through all of the trouble.
Section 3.3 of the CDI specification gives a pretty good high level overview of the use of the #Produces annotation:
A producer method acts as a source of objects to be injected, where:
• the objects to be injected are not required to be instances of beans, or
• the concrete type of the objects to be injected may vary at runtime, or
• the objects require some custom initialization that is not performed by the bean constructor.
Let's say, for example, that you wanted to bridge between a Java EE managed component like an entity manager and other CDI components, you could utilize the #Produces annotation. Another benefit being that you avoid having to duplicate #PersistenceContext annotations throughout your data domain layer.
class A {
#PersistenceContext // This is a JPA annotation
#Produces // This is a CDI 'hook'
private EntityManager em;
}
class B {
#Inject // Now we can inject an entity manager
private EntityManager em;
}
Another handy use is for getting around libraries that do not have CDI friendly beans (for example, no default constructors):
class SomeTPLClass {
public SomeTPLClass(String id) {
}
}
class SomeTPLClassProducer {
#Produces
public SomeTPLClass getInstance() {
return new SomeTPLClass("");
}
}
The Javadoc for produces also shows an interesting (but fairly rare case) of producing a named collection that can later on be injected into other managed beans (very cool):
public class Shop {
#Produces #ApplicationScoped
#Catalog #Named("catalog")
private List<Product> products = new LinkedList<Product>(8);
//...
}
public class OrderProcessor {
#Inject
#Catalog
private List<Product> products;
}
The container is responsible for processing all methods and fields marked with a #Produces annotation, and will normally do this when your application is deployed. The processed methods and fields will then be used as part of the injection point resolution for managed beans, as needed.
The example didn't quite work for me. What dit work was a minor tweak:
#Alternative
class SomeTPLClass {
public SomeTPLClass(String id) {
}
}
class SomeTPLClassProducer {
#Produces
public SomeTPLClass getInstance() {
return new SomeTPLClass("");
}
}
So i had to add #Alternative on my class to get rid of the error that there were two options for #Default.

Categories

Resources