I am struggling to understand how I am supposed to deal with JTA and CDI running on a Jboss EAP 7 instance. I can get a transaction manually by injecting a UserTransaction object coming from the container but when I annotate the method with the #Transactional I get an exception regarding no transaction available.... My question is. Is there any config missing? I read briefly that maybe I should create an interceptor myself in order to make it work, but I haven't found any consistent example...
In a default JEE container-managed environment, only enterprise beans (usually #Stateless beans are used) are transactional. Once you enter such a bean from outside, the transaction will be opened. With the #javax.transaction.Transactional annotation you can control the behavior of the transactions, but this is not necessary in default case.
Example bean:
#Stateless
public MyBean {
public void withinTransaction() {
System.out.println("i'm running within a transaction");
}
#Transactional(TxType.NOT_SUPPORTED)
public void outsideTransaction() {
System.out.println("no transaction available...");
}
}
If you call MyBean.withinTransaction from a Servlet (e.g. via REST), a new transaction is created (if not already present).
If you call MyBean.outsideTransaction, no transaction will be created.
If you call this.outsideTransaction() from withinTransaction, you will still have the transaction available in outsideTransaction (because the interceptors are only bound to the bean boundaries)
If you call this.withinTransaction() from outsideTransaction no new transaction is created (because the interceptors are only bound to the bean boundaries)
If outsideTransaction would be part of a second bean AnotherBean, which #Injects MyBean, and you call MyBean.withinTransaction, then a new transaction will be created (if not already present). Because you cross bean boundaries between AnotherBean.outsideTransaction and MyBean.withinTransaction.
Related
For various reasons I need to perform a manual lookup of SessionContext. In JBoss5, the solution
InitialContext initialContext = new InitialContext();
SessionContext sessionContext = (SessionContext) initialContext.lookup("java:comp/EJBContext");
has served med well, but from JBoss 7 I instead get a
javax.naming.NameNotFoundException: EJBContext -- service jboss.naming.context.java.global.EJBContext
Has something changed in how context is looked up in JBoss 7.2, or is my deployment lacking anything vital? For reference, standard injection works fine, this is the only lookup that fails. Or am I doing something terribly wrong (besides performing a manual lookup of SessionContext)?
According to specification of Java EJB (this one is for EJB 3.2. but nothing changed about EJBContext from previous one, EJB 3.x), you can inject EJBContext into your components either using annotation #Resource or manually via lookup (section 11.15):
The container must make a component’s EJBContext interface available either through injection
using the Resource annotation or in JNDI under the name java:comp/EJBContext
Standard way of looking up for EJB resource is via EJBContext.lookup method but there is also JNDI way which is the only possibilities if you don't have already EJBContext:
Context initCtx = new InitialContext();
EJBContext ejbCtx = (EJBContext) initCtx.lookup("java:comp/EJBContext");
This is exactly what you did, so what is wrong? There are two things, which one I'm not sure about. First, with manually lookup it's sometime needed to assign resource to component with annotation at class level:
#Resource(name = "EJBContext", type = javax.ejb.EJBContext)
public class MyComponent {
...
}
but I'm not sure is it needed for EJBContext as well, I guess not. The second thing, more important and critical - according to specification once again:
EJBContext objects accessed through the naming environment are only valid within the bean
instance that performed the lookup.
this one is section 11.15.1, and the next one, section 11.15.2:
The Container Provider is responsible for providing an appropriate EJBContext object to the refer-
encing component. The object returned must be of the appropriate specific type for the bean requesting
injection or performing the lookup—that is, the Container Provider must return an instance of the SessionContext interface to referencing session beans and an instance of the MessageDrivenCon-
text interface to message-driven beans.
Those both mean that injection and lookup for EJBContext are only valid in Enterprise Java Beans, so those which are annotated with #MessageDriven, #Stateful, #Singleton or #Stateless (or described as EJBs in deployment descriptor file, also as EJB 2.x Specification). Maybe your component isn't valid EJB and it's why lookup isn't working? This is only suggestion of course.
There's one more possibilities to get EJBContext (more correctly SessionContext). Your component should implements SessionBean interface which has setSessionContext(SessionContext sessionContext) method. This method should be invoked by EJB container every time when component is used (injected somewhere, invoked by client or timeout, especially when it's created) and inside this method you should assign sessionContext parameter to bean's field.
Somehow this piece of information seems to be missing in the usual Java EE documentatation resources:
Unless there is an external transaction active, the container starts a new transaction when calling a business method of an EJB with container managed transactions, if this business method is assigned a transaction attribute that requires a transaction (e.g. TransactionAttributeType.REQUIRED).
Now, if this method call is intercepted by a method interceptor
#AroundInvoke
public Object onMethodCall(InvocationContext ctx) throws Exception
{
doSomethingBefore();
ctx.proceed();
doSomethingAfterwards();
}
Will the methods doSomethingBefore() and doSomethingAfter() be called within this new transaction context, or will the transaction be restricted to the call ctx.proceed()?
From the Javadoc for #AroundIvoke:
AroundInvoke method invocations occur within the same transaction and security context as the method on which they are interposing.
I need to commit transactions from CMT bean by hand. There is a loop which processes multiple records and each record should be processed in its own transaction. I wanted to mark method transaction support as NOT_SUPPORTED and then control transaction from method. However I could not retrieve a UserTransaction instance neither from SessionContext neither injecting it as a JNDI resource java:/module/UserTransaction.
Are there any chance to process multiple records in CMT bean in their own transactions without introducing new BMT bean for such processing?
You should not mess around transactions yourself if you use CMT.
I recommend you create a method for the operation needs to be in transaction, mark it as REQUIRES_NEW, then call it from the loop.
Everytime the method is called, the current transaction (if any) will be suspended and a new transaction will be started for the operation.
Something like this:
#EJB
SomeEJBLocal anotherme;
public void loop() {
for(/* something */) {
anotherme.single();
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void single() {
// do stuff
}
You will have to inject another instance of the EJB and call single in order for the container to process the transaction aspects.
We have a simple stateless EJB timer that gets an instance of a spring service injected into it. The spring service has a method marked as transactional. When the EJB uses Transaction Management Type CONTAINER the spring service call results in an IllegalStateException: Operation not allowed. So we set the EJB Transaction Management Type to be BEAN and everything works as advertised. Is this normal? I was under the impression that the spring transaction management would join the CMT.
example EJB
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
#Interceptors(SpringBeanAutowiringInterceptor.class
public class TimerService {
#Autowired
IHelloService helloService;
#Schedule(second="*/1", minute="*",hour="*", persistent=false)
public void doWork(){
helloService.hello();
}
}
example spring service
#Service
public class HelloService implements IHelloService {
#Transactional
public void hello(){
}
}
Probably the problem can be fixed, if you would define propagation level for Spring transaction as SUPPORTS (default is REQUIRED). I do not remember exact value for default settings in EJB, but it seems like EJB method is not starting transaction (propagation is DEFAULT), but 'hello' tries to do so and that is causing conflict.
I'll try to describe the situation. We have a web service; on each request web service starts a JTA transaction. It performs several database calls through XA datasource within that and calls some other web services (out of transaction context) and also makes a couple of remote EJB calls on other server.
The problem is that container seems to try to involve EJB into transaction (and that seems logical), but in fact I want it to not particpate in that transaction as when it does participate in that transcation it always times out in the final commit phase, but when I exclude EJB call it works fine.
I cann't change EJB implementation and only control web service code. So, my question is: how do I make an EJB call to transaction-aware EJB, but out of my JTA transaction, but still being in JTA transaction for other XA resourse? I hope I made my question clear :).
EDIT: Trying to make it more clear with pseudo-code example:
// Begin transaction
UserTransaction tx = (UserTransaction) ctx.lookup(USER_TRANSACTION);
tx.begin();
// Do some database operations on XA datasource
// Call remote EJB which has transcation attribute set to 'Supports'
AccountInfo account = accountEjb.getAccountInfo(userId, accountId); // <-- Is it possible to make this to be not be part of user transction?
// Do some more database operations on XA datasource
// Commit transaction
tx.commit();
You can create another bean with some appropriate transaction attribute. This bean can delegate all calls to the first bean.
Or you can invoke this ejb from another thread.
EJB transaction is declarative: for a given deployment of a given EJB, you specify its transaction semantics. The exact EJB can be deployed (under a different name, of course) and you can specify different requirements for that deployment. This is assuming that (a) you at least have the jar for the ejb, and, (b) that the ejb in question is stand alone and doesn't have dependencies on other components, and (c) the developer of the ejb hasn't violated the idea of the declarative transactions and his bean works outside of a transaction context as well.
You can create another method with the suitable tx attribute and then call it with through the self-injected proxy (pseudo-code):
#Stateless
public class LocalEJB1 {
#EJB
private LocalEJB1 localEJB1;
#EJB
private AccountEJB accountEjb;
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public AccountInfo callNonTx() {
return accountEjb.getAccountInfo(userId, accountId);
}
public void yourCurrentMethod() {
// Begin transaction
UserTransaction tx = (UserTransaction) ctx.lookup(USER_TRANSACTION);
tx.begin();
AccountInfo account = localEJB1.callNonTx();
// Do some more database operations on XA datasource
// Commit transaction
tx.commit();
}
}