I'm having difficulty with my EJB3.1 bean initialisation and more specifically to do with it failing due to perceived transaction rollback, even though I've marked the bean with @TransactionAttribute(NOT_SUPPORTED)
. This should mean that any client transaction is paused on bean method entry until exit (when it will be resumed. It's definitely the transactional apprach I want.
The "gist" of the code and error are as follows (note some of it is hand-cranked to hide details but is all relevant and representative):
@Singleton(name = "MyClass")
@ConcurrencyManagement(value = BEAN)
@TransactionAttribute(value = NOT_SUPPORTED)
@Local(MyInterface.class)
public class MyClass implements MyInterface {
@PostConstruct
public void init() throws MyException {
try {
...
} catch LowerLevelException e) {
throw new MyException("problem", e);
}
}
public Object doSomething(...) throws MyException {
...
}
...
}
This raises the following error:
javax.ejb.NoSuchEJBException: Singleton MyClass(Application: my-ear, EJBComponent: my-ejb.jar) failed to initialize.
which, when I debug is actually wrapping the following exception:
weblogic.ejb.container.InternalException: Transaction marked rollback or not expected transaction status: 4
Again, debugging raises some interesting details:
MyClass#init
is being executed completely and successfully without any issues/ exceptions.#init
is being called on first call to my #doSomething
method from client code (as part of post-construct).MyClass#doSomething
down to #init
and the exception is raised inside of one of these layers. It's raised in WLS Singleton session bean management code. See below...The stack resembles that below (MyClass names changed):
MyClass_fefgu8_Impl(MyClass).init() line: 96
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 601
Jsr250Metadata.invokeLifecycleMethod(Object, Method, Object[]) line: 393
InterceptionMetadata(Jsr250Metadata).invokeLifecycleMethods(Object, LifecycleEvent) line: 365
InterceptionMetadata.invokeLifecycleMethods(Object, LifecycleEvent) line: 403
EjbComponentCreatorImpl.invokePostConstruct(Object, String) line: 80
SingletonSessionManager.constructAndInitBean() line: 369
SingletonSessionManager.access$300(SingletonSessionManager) line: 63
SingletonSessionManager$SingletonLifecycleManager.doActualInit() line: 798 <== InternalException raised here
SingletonSessionManager$SingletonLifecycleManager.initInternal(boolean) line: 744
SingletonSessionManager$SingletonLifecycleManager.getBean() line: 648
SingletonSessionManager.getBeanFor(InvocationWrapper) line: 285
SingletonSessionManager.preInvoke(InvocationWrapper) line: 147
SingletonLocalObject(BaseLocalObject).getBeanInstance(InvocationWrapper) line: 146
SingletonLocalObject(BaseLocalObject).preInvoke(InvocationWrapper, ContextHandler, boolean, boolean) line: 103
SingletonLocalObject(BaseLocalObject).__WL_preInvoke(InvocationWrapper, ContextHandler) line: 67
SessionLocalMethodInvoker.invoke(Invokable, BaseLocalObject, InvocationWrapper, Object[], int) line: 20
MyClass_fefgu8_MyInterfaceImpl.doSomething(String, String, String, String) line: not available
And the Transaction rollback exception is actually raised in stack-frame:
SingletonSessionManager$SingletonLifecycleManager.doActualInit() line: 798
This level does have dealing with a transaction manager but I've no idea why it should be trying to mark a transaction for rollback, especially after a successful init from the main application code.
I'm using this EJB component to compile this bean but we use WLS EJB code on the server (or course):
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.ejb</artifactId>
<version>3.1</version>
<scope>provided</scope>
</dependency>
The reason I've put this out there is that it's proving tricky, there's precious little on-line (especially with transaction status 4), I've spent a bit too long on it and need to move on. I'm carrying on looking but I thought I might get more leads for when I'm back on Monday.
I've re-read a lot around EJB transaction handling and don't see any obvious issues. I believe I'm using the correct mechanism for handling the transaction in my bean (i.e. it's non-transactional). I've not yet tried putting it in the descriptor instead (which I just thought of and will give a go). I've also not yet delved deeply into howSingletonSessionManager$SingletonLifecycleManager.doActualInit
works but there's not a lot on-line about it.
The question
To rephrase, the question is... - What am I missing so that my bean methods are not participating in any supplied transaction, or more specifically, not trying to mark a transaction for rollback? - Why should it fail to initialise with this error if I've explicitly told it not to "support" (care about) any transactions?
N.B. I've checked this @Singleton bean failed to initialize because of not expected transaction status 1 issue but my scenario doesn't relate to Java EE security role permissions (I don't think!)
Thanks.
Update 1
Well, the latest is that removing my @TransactionAttribute
annotation seems to get me past the failure. This is strange though as the default should be TransactionAttributeType#Required and indeed, we have an additional layer in the stack trace and if I debug too long then I get a transaction timeout in my bean init.
Looking at the following level in the stack-trace (slowly nexting out), I see that there is a transaction (well, two, actually):
SingletonSessionManager.constructAndInitBean() line: 377
wrap weblogic.ejb.container.internal.InvocationWrapper (id=15676)
L callerTx weblogic.transaction.internal.ServerTransactionImpl (id=15681)
L invokeTx weblogic.transaction.internal.ServerTransactionImpl (id=15683)
I'll check if these were present in the pre-remove @TransactionAttribute
scenario and also try and watch ServerTransactionImpl
.
Update 2
Last update for a while... However, I think I've traced the issue into SingletonSessionManager#postCallback
which is called from SingletonSessionManager#constructAndInitBean
in the original stack-trace above. (I'd need to confirm this though as my lead is determined by running through the transactional case). Will report back if/ when but it seems we may have a failure trying to rollback our non-existent transaction in here.
N.B. This was not originally an answer but just some more useful info in digging in to find answers to such problems as there really isn't much on-line in this area. After some additional effort, the cause and an answer have since been found. The answer has been left in its original format with new findings documented as edits and debug help left in-tact to hopefully ease the debugging of others with similar issues. This is a dark place.
Well, still no firm answer to the above issue and the problem seems to have gone away.
I have found though (looking at a different NoSuchEJBException
issue) that the underlying cause is swallowed by the WLS stack.
This is the sensible place to start looking for SingletonBean failed to init NosuchEJBException issues in cases where you don't make it into your PostConstruct
method (i.e. your bean could not be created):
SingletonSessionManager$SingletonLifecycleManager.doActualInit() line: 819
SingletonSessionManager$SingletonLifecycleManager.initInternal(boolean) line: 744
SingletonSessionManager$SingletonLifecycleManager.getBean() line: 648
SingletonSessionManager.getBeanFor(InvocationWrapper) line: 285
Initially, root exceptions are caught and included into the NoSuchEJBException
s raised in the lower levels, as above. However, at the point in the stack below, the NoSuchEJBException
is replaced with an InternalError
that gets wrapped and the cause(s) lost. Curiously, it may even set itself as the cause (debugger goes a bit haywire):
SingletonSessionManager#getBeanFor
So, this is a prime place for finding what the root exception may be.
FYI: My problem in the second instance was a spring-injection issue within WLS (spring-repackaged) where a datasource wasn't beig properly injected into my ELB service impl as the datasource didn't exist with the @Resource(name="...")
JNDI name. This wasn't logged at all due to the aforementioned exception-handling issue. Once I'd stepped through and found the cause, it was an easy fix. Shame to have to spend so much time finding it out.
[EDIT] FYI: It seems my problem causing the "InternalException Transaction Rolled-back status 4" exception may have been due to the EJB init (PostConstruct) exceeding the transaction timeout. This should be irrelevant if my EJB is set as @TransactionAttribute(NOT-SUPPORTED)
but apparently not so. Debugging revealed that for this and @TransactionAttribute(NEVER)
, there is an invokeTx
used at the level of the SingletonSessionManager#constructAndInitBean
and managed within the InvocationWrapper
. So, the timeout would cause the rollback, leading to the InternalException
, mapped to the NoSuchEJBException
, swallowed by the other NoSuchEJBException
leading to a rubbish error message.
[EDIT2] FYI: It seems this is due to a bug in WLS where the invokeTx is set and managed when it shouldn't be for @TransactionAttribute(NOT-SUPPORTED)
. We've raised a bug with Oracle who have given it a sev 2. In the meantime, we've moved potentially time-consuming (network-dependent) code out of the @PostConstruct
method as a workaround.
[EDIT3] FYI: From Oracle ... The class level transaction attribute annotation is not enforced on singleton lifecycle interceptors postconstrcut/predestroy, so it defaults to REQUIRED.
They concur that this is an issue and are looking into it. In the meantime, ensure transaction timeouts aren't breached by code within your @PostConstruct
. This would include latency-based timeouts (our issue was intermittent as it depended on which geographical "server" was accessed).
Debugging tips
As a debugging process, btw, I suppose I would:
@PostConstruct
) method and unwind until you see the exception occur in your debug values.#doActualInit
listed above and find your way into the init methods. then, just watch for when the exception is first raised. Note, you may have to put your breakpoint inside InvocationHandler#invoke
to get near this through the proxy layers (or roll your own).init
and unwinding or in EjbComponentCreatorImpl.invokePostConstruct(Object, String)
and watching.