Details
-
Bug
-
Status: Resolved
-
Critical
-
Resolution: Not A Problem
-
1.5.0, 1.5.1
-
None
-
None
-
None
-
Windows 7 x64; jdk1.7.0_09 32 bit; apache-tomee-1.5.1-20121026.064316-51-webprofile.zip
Description
1. Create class EntityManagerProducer:
@SessionScoped
@Stateful
public class EntityManagerProducer implements Serializable {
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;
@Produces
public EntityManager getEntityManager()
}
2. Create injection client:
@Named
public class A {
@Inject
private EntityManager em;
public String getDelegateClassName()
{ return em.getDelegate().getClass().getCanonicalName(); }}
3. Create JSF page and try to call getDelegateClassName():
<h:body>
EntityManager is open: #
</h:body>
4. Deploy (successful) and run the application - observe error message (printed in browser):
An Error Occurred:
InternalError: an entity manager should already be registered for this extended persistence unit
viewId=/index.xhtml
location=D:\dev\proj\templates\InjectionDemo\build\web\index.xhtml
phaseId=RENDER_RESPONSE(6)
Caused by:
java.lang.IllegalStateException - InternalError: an entity manager should already be registered for this extended persistence unit
at org.apache.openejb.persistence.JtaEntityManagerRegistry.getEntityManager(JtaEntityManagerRegistry.java:99)
- Stack Trace
java.lang.IllegalStateException: InternalError: an entity manager should already be registered for this extended persistence unit
at org.apache.openejb.persistence.JtaEntityManagerRegistry.getEntityManager(JtaEntityManagerRegistry.java:99)
at org.apache.openejb.persistence.JtaEntityManager.getEntityManager(JtaEntityManager.java:80)
at org.apache.openejb.persistence.JtaEntityManager.getDelegate(JtaEntityManager.java:114)
at org.apache.openejb.persistence.JtaEntityManager.getDelegate(JtaEntityManager.java:53)
at beans.A.getDelegateClassName(A.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
Attachments
Activity
This is not CDI related. Following example uses only EJB 3.1 functionality, yet still throws the same exception:
---------------------------------------
@Stateful
public class EntityManagerProducer implements Serializable {
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;
public EntityManager getEntityManager()
{ return em; }}
@Stateless
public class A implements {
@EJB
private EntityManagerProducer entityManagerProducer;
public String getDelegateClassName()
{ return entityManagerProducer.getEntityManager().getDelegate().getClass().getCanonicalName(); }}
---------------------------------------
I checked both EJB and JPA specifications: there is no restriction to USE (not to create) Extended entityManager outside the stateful EJB. I understand that this is dangerous (yet very usefull implementing conversations), but this is not specification compliant. And by the way, this is working OK in Glassfish.
Should I fill the bug for OpenEJB? For what version?
This worked for me on trunk
BTW you shouldn't inject a stateful with @EJB, you should do a lookup to get it
side note about the original error: about your exception: such an entity manager (of a stateful) only exist if the stateful exist, if you produce the em (without scope??) the stateful hasn't to exist (and that's the case you had) so the real em doesn't exist too. That's why you got this error which is normal.
Finally about "there is no restriction": in specification you should have a look to the opposite: "is it explicitely allowed". Not forbidden doesn't mean allowed (that's a pain but that's already better this way than without any spec ).
> BTW you shouldn't inject a stateful with @EJB, you should do a lookup to get it
Yes, I know This was artificial to just create an example without CDI.
I'll check with the most recent snapshot too.
> This worked for me on trunk
Sorry, I trimmed the example too much (took some time for me to nail it down). This one is not working on todays snapshot version:
@Named
@Stateless
public class A {
private EntityManager em;
@EJB
private EntityManagerProducer entityManagerProducer;
public String getDelegateClassName()
{ //return entityManagerProducer.getEntityManager().getDelegate().getClass().getCanonicalName(); // <-- WORKING OK return em.getDelegate().getClass().getCanonicalName(); // <-- NOT WORKING } @PostConstruct
public void construct()
}
@Stateful
public class EntityManagerProducer implements Serializable {
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;
public EntityManager getEntityManager()
{ return em; }}
index.xhtml:
<h:body>
EntityManager delegate: #
</h:body>
> such an entity manager (of a stateful) only exist if the stateful exist, if you produce the em (without scope??) the stateful hasn't to exist (and that's the case you had) so the real em doesn't exist too
Nope, real process is different, as per CDI spec:
5.5.4. Invocation of producer or disposer methods
When the container calls a producer or disposer method, the behavior depends upon whether the method is static or nonstatic:
• If the method is static, the container must invoke the method.
• Otherwise, if the method is non-static, the container must:
- Obtain a contextual instance of the bean which declares the method, as defined by Section 6.5.2, “Contextual instance
of a bean”. - Invoke the method upon this instance, as a business method invocation, as defined in Section 7.2, “Container invocations
and interception”.
So, stateful EJB is being constructed before producer method invocation (and put to respective CDI context for further reuse).
which annotation on A.em? (doesn't appear in JIRA comment, not sure that's a copy/paste error or a jira front issue)
> which annotation on A.em?
None This is just temporary variable (could be made transient too).
well your example still sounds weird to me since if you imagine you have multiple instance of your em holder you can't know which em reference you expect when calling em in your code (well i understood what you expected but the spec speaks about extended em inheritance which is not always what you expect)
so for me it is still not so clear
wdyt?
I'll try to explain why I'm so persistent
Imagine you'd like to implement some complicated/complex use-case that is of conversation type (i.e. some wizard, checkout procedure, etc.). By complicated/complex I mean that this use-case consist of 3-4 steps (imagine wizard steps) and EACH of them requires entering much of data and/or complex processing of that data and communication with DB. So approach to have one component implementing the whole use-case is not correct from OOAD/coupling/cohesion perspective. Programmer would be tended to have multiple components, for example, one for each use-case step (or at least more than one component). Now this is no longer documented obviously in EJB/JPA/CDI specs. I see two ways to implement such a use-case:
1. "Propagated extended EntityManager" way - one Facade @ConversationScoped Stateful EJB delegating business logic to other components.
a) Create Stateful @ConversationScoped EJB that governs the conversation (but does not have business logic for separate steps). It declares @PersistenceContext(EXTENDED) EntityManager em (though might not use it at all itself). Lets call it Facade EJB.
Implement all the business logic methods within this Facade EJB that delegate calls to other business logic components.
b) Create other business logic components (Stateless or @RequestScoped Stateful, depending on business logic semantics). They declare @PersistenceContext(TRANSACTIONAL), but always receive EntityManager from Facade EJB by PersistenceContext propagation/inheritance
c) Create JSF page for each use-case step.
d) Apply strict limitations: all JSF pages are allowed to call ONLY Facade EJB (otherwise EntityManager will not be propagated); Facade EJB calls business logic components; business logic components do work and communicate with DB. Such limitations ensure, that all business components during the whole conversation are working with one and the same EntityManager instance (managed by Facade EJB).
If somebody thinks that limitations above are too strict (programmer thinks: "I want my JSF page to communicate with business logic component directly - I'm tired to write hundreds of business logic delegate methods within Facade EJB; CDI allows me to get rid of delegate methods, doesn't it???"):
2. "Injected extended EntityManager" way
a) Create Stateful @ConversationScoped EJB that governs the conversation (but does not have business logic for separate steps). It declares "@PersistenceContext(EXTENDED) EntityManager em" and has a producer that returns this particular instance (NOT a new instance each time!!!) NO business logic delegate methods.
b) Create other business logic components (Stateless or @RequestScoped Stateful, depending on business logic semantics). They declare "@Inject EntityManager em". In real-life we would create different qualifiers for different conversations (use-cases) too (I would like future CDI to support limited scope/namespace/module injection - not to inject anything from the whole world)
c) Create JSF pages
d) Almost none limitations: As soon as one JSF page touches busines logic component, it asks for injected EntityManager, Stateful @ConversationScoped bean gets created, produces EntityManager, and so on... (of course, conversation.begin() needs to be called, but this is not a problem).
No. 2 is better, because:
- no delegate methods are needed
- JSF pages call business components directly - less sophisticated code.
Up to now only Glassfish allows me to implement conversation use-case in 2. way.
> Did you try using a stateful as entry point?
That would be option No. 1 (above) - Yes, it seems to work OK with Tomee.
can match 1 or 1bis : stateful can hold business code. What i meant is stateful are not evil and can be used as any other bean. This way you avoid the side effects you can get with 2
I'm not sure I got your idea. I'm not against stateful beans - option 1 is using stateful beans for business logic (Facade statul ejb call/delegates to business stateful ejbs).
My point was that single stateful ejb sometimes is not enough - you want to implement complex use-case with several OO classes. Question is how to bind them together.
Personally i let a stateful matching a wizard but depend on your architecture too
FYI: similar GlassFish issue was fixed:
http://java.net/jira/browse/GLASSFISH-11805
I have asked EJB experts for their opinion:
http://java.net/projects/ejb-spec/lists/users/archive/2012-11/message/0
Ok great,
FYI you should be able to produce the em now even with session scope (but it will still fail later because the stateful was not touched)
Actually it would be really enough if this would work with @ConversationScoped @Stateful. As was noted by Mark, nobody want @SessionScoped EntityManager (and it makes no sense).
Thanks, I'll try it out with the next snapshot.
right replace session by passivable scope in my previous sentence
Tomee snapshot building process seems to be down, the last snapshot is of october-29.
EJB experts did not answer to my question. I've posted the question to JPA 2.1 spec users mailing list:
http://java.net/projects/jpa-spec/lists/users/archive/2013-01/message/51
I've create a JPA JIRA issue "Explicitly allow or disallow use of Entity Manager with extended Persistence Context for CDI injection":
http://java.net/jira/browse/JPA_SPEC-46
ldemichiel responded: I think this is probably more an EJB issue than a JPA one, but in any case it is something that we should evaluate for the next release.
Lets close this with "Invalid" or something similar.
probably means the statful doesn't exist
since the extended em only exist in the stateful context using it outside (in cdi) is not as obvious as it can seem