Acording to another post [1] there's no difference between invoking a session EJB via JNDI lookup and using the #EJB annotation. However, in the following scenario:
1.- call session EJB1(JDBC inserts here)
2.- From EJB1, call session EJB2 (more inserts here)
3.- Rollback the transaction (from EJB1)
If I use the #EJB annotation it works fine, but with the JNDI lookup it doesn´t, the transaction in the second EJB is a new one and the rollback doesn´t happen. All this with CMT.
I'm deploying all this stuff in a Geronimo/ibmwasce-2.1.1.6.
¿Do I need to pass the transaction from one EJB to another explicitly? I thought it was the continer job. ¿Any clues?
[1] #EJB annotation vs JNDI lookup
Update:
Code via annotation:
#EJB
private CodAppEjb codAppejbAnotacion;
Code via jndi:
CodAppEjb codAppejb;
InitialContext ctx;
Properties properties= new Properties();
properties.setProperty("java.naming.provider.url", "ejbd://127.0.0.1:4201");
properties.setProperty("java.naming.factory.initial", "org.apache.openejb.client.RemoteInitialContextFactory");
ctx = new InitialContext(properties);
codAppejb= (CodAppEjb) ctx.lookup("CodAppEjbBeanRemote");
The transaction code is just the same.
It seems, you have a transaction propagation problem.
The problem seems to be, that in your JNDI lookup you search for the remote EJB (not Local), which does NOT get executed in the same transaction context as EJB1.
When using the #EJB annotation above, the local implementation is injected, with the same transaction context.
Related
I am trying to migrate an application from EJB3 + JTA + JPA (EclipseLink). Currently, this application makes use of application managed persistent context due to an unknown number of databases on design time.
The application managed persistent context allows us to control how to create EntityManager (e.g. supply different datasources JNDI to create proper EntityManager for specific DB on runtime).
E.g.
Map properties = new HashMap();
properties.put(PersistenceUnitProperties.TRANSACTION_TYPE, "JTA");
//the datasource JNDI is by configuration and without prior knowledge about the number of databases
//currently, DB JNDI are stored in a externalized file
//the datasource is setup by operation team
properties.put(PersistenceUnitProperties.JTA_DATASOURCE, "datasource-jndi");
properties.put(PersistenceUnitProperties.CACHE_SHARED_DEFAULT, "false");
properties.put(PersistenceUnitProperties.SESSION_NAME, "xxx");
//create the proper EntityManager for connect to database decided on runtime
EntityManager em = Persistence.createEntityManagerFactory("PU1", properties).createEntityManager();
//query or update DB
em.persist(entity);
em.createQuery(...).executeUpdate();
When deployed in a EJB container (e.g. WebLogic), with proper TransactionAttribute (e.g. TransactionAttributeType.REQUIRED), the container will take care of the transaction start/end/rollback.
Now, I am trying to migrate this application to Spring Boot.
The problem I encounter is that there is no transaction started even after I annotate the method with #Transactional(propagation = Propagation.REQUIRED).
The Spring application is packed as an executable JAR file and run with embadded Tomcat.
When I try to execute those update APIs, e.g. EntityManager.persist(..), EclipseLink always complains about:
javax.persistence.TransactionRequiredException: 'No transaction is currently active'
Sample code below:
//for data persistence
#Service
class DynamicServiceImpl implements DynamicService {
//attempt to start a transaction
#Transactional(propagation = Propagation.REQUIRED)
public void saveData(DbJndi, EntityA){
//this return false that no transaction started
TransactionSynchronizationManager.isActualTransactionActive();
//create an EntityManager based on the input DbJndi to dynamically
//determine which DB to save the data
EntityManager em = createEm(DbJndi);
//save the data
em.persist(EntityA);
}
}
//restful service
#RestController
class RestController{
#Autowired
DynamicService service;
#RequestMapping( value = "/saveRecord", method = RequestMethod.POST)
public #ResponseBody String saveRecord(){
//save data
service.saveData(...)
}
}
//startup application
#SpringBootApplication
class TestApp {
public static void main(String[] args) {
SpringApplication.run(TestApp.class, args);
}
}
persistence.xml
-------------------------------------------
<persistence-unit name="PU1" transaction-type="JTA">
<properties>
<!-- comment for spring to handle transaction??? -->
<!--property name="eclipselink.target-server" value="WebLogic_10"/ -->
</properties>
</persistence-unit>
-------------------------------------------
application.properties (just 3 lines of config)
-------------------------------------------
spring.jta.enabled=true
spring.jta.log-dir=spring-test # Transaction logs directory.
spring.jta.transaction-manager-id=spring-test
-------------------------------------------
My usage pattern does not follow most typical use cases (e.g. with known number of DBs - Spring + JPA + multiple persistence units: Injecting EntityManager).
Can anybody give me advice on how to solve this issue?
Is there anybody who has ever hit this situation that the DBs are not known in design time?
Thank you.
I finally got it work with:
Enable tomcat JNDI and create the datasource JNDI to each DS programmatically
Add transaction stuff
com.atomikos:transactions-eclipselink:3.9.3 (my project uses eclipselink instead of hibernate)
org.springframework.boot:spring-boot-starter-jta-atomikos
org.springframework:spring-tx
You have pretty much answered the question yourself: "When deployed in a EJB container (e.g. WebLogic), with proper TransactionAttribute (e.g. TransactionAttributeType.REQUIRED), the container will take care of the transaction start/end/rollback".
WebLogic is compliant with the Java Enterprise Edition specification which is probably why it worked before, but now you are using Tomcat (in embedded mode) which are NOT.
So you simply cannot do what you are trying to do.
This statement in your persistence.xml file:
<persistence-unit name="PU1" transaction-type="JTA">
requires an Enterprise Server (WebLogic, Glassfish, JBoss etc.)
With Tomcat you can only do this:
<persistence-unit name="PU1" transaction-type="RESOURCE_LOCAL">
And you need to handle transactions by your self:
myEntityManager.getTransaction.begin();
... //Do your transaction stuff
myEntityManager.getTransaction().commit();
I am facing stackoverflow error when I inject ejb using #EJB or JNDI lookup using InitialContext. Can anybody share kow to inject/lookup EJB from SevletContextListener in JBoss AS 6.
Thanks!
For lookup, you can do something similar to this
InitialContext ctx =new InitialContext();
EjbServiceInterface service= (EjbServiceInterface) ctx.lookup("java:global/earName/ejbJarName/EjbServiceInterfaceImpl!com.example.EjbServiceInterface");
In this example, EjbServiceInterface is a remote interface for EjbServiceInterfaceImpl, which implements the ejb service.
The jndi string depends on the place where you are doing the lookup.. For a local lookup, you don't have to specify earName and ejbJarName.. I recommend looking in your startup log, where it shows the jndi names available for your ejb, and then select the one you need.
I already have a integration-test phase, when I ran the selenium tests. I also want to run some unit tests in this phase, because the app is too much complex and have a lot of dependencies between his modules (a hell), so, after a week fighting against OpenEJB and Arquillian, I believe that this would be easier.
The thing is: how do I made it work?
I have the instance already running, if I instantiate an InitialContext and try to lookup some bean, I got an exception telling me that I have not set the java.naming.initial.factory, and I don't know what to put in there.
I'm also complaining about the annotated beans.
Suppose a Bean like this:
#Stateless
public class ABeanImpl implements ABean {
#EJB
private BBean;
}
Will the container automatically get right the BBean?
Thanks in advance
How to connect to JBoss 7.1 remote JNDI:
Here is the code snippet that I use for JBoss 7.1:
Properties props = new Properties();
String JBOSS_CONTEXT = "org.jboss.naming.remote.client.InitialContextFactory";
props.put("jboss.naming.client.ejb.context", true);
props.put(Context.INITIAL_CONTEXT_FACTORY, JBOSS_CONTEXT);
props.put(Context.PROVIDER_URL, "remote://localhost:4447");
props.put(Context.SECURITY_PRINCIPAL, "jboss");
props.put(Context.SECURITY_CREDENTIALS, "jboss123");
InitialContext ctx = new InitialContext(props);
Resolution of ambiguous ejb references:
According to JBoss EJB 3 reference, if at any level of your EJB environment (EJB/EAR/Server) are duplicates in used interfaces, exception will be thrown during resolution of injected beans.
Based on above, if you have got a reference to EJB bean which interface:
has two implementations in your EJB module (JAR/WAR) - exception will be thrown
has two implementations in your application (other EJB JAR's in same EAR) - exception will be thrown
has two implementations, one in module with bean ABeanImpl, second somewhere else - implemetation from current module is used.
I am running Glassfish 3.1.1 with an Oracle database and have run into an issue with transactions not rolling back, but only on one specific environment so far. The same application works as expected on other machines. However, two separate Glassfish domains on the same machine are impacted.
Within the affected environment, I have similar results with both a container-managed transactions (CMT) inside an EJB that throws a RuntimeException, and a bean-managed transaction (BMT) with UserTransaction#rollback().
In both cases, the underlying issue appears to be that the JDBC connection is somehow still set with autoCommit = true even though there is a JTA transaction in progress.
My EJB/CMT test looks like this:
#Named
#Stateless
public class TransactionTest {
#PersistenceContext
EntityManager entityManager;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void rollbackTest() {
Foo foo = new Foo();
entityManager.persist(foo);
entityManager.flush();
throw new RuntimeException("should be rolled back");
}
}
and my BMT/UserTransaction test is like this:
public void rollbackUtxTest() throws Exception {
utx.begin();
Foo foo = new Foo();
entityManager.persist(foo);
entityManager.flush();
utx.rollback();
}
When I call either method, the INSERT INTO FOO is committed, even though the transactions were rolled back.
What am I missing - perhaps I don't have my connection pool / datasource is not set up right?
I'm using OracleConnectionPoolDataSource as the datasource class name. Is there something I need to do to ensure my database connections participate in JTA transactions?
UPDATE 1 I originally thought this was an issue with OracleConnectionPoolDataSource but it turned out it was not correlated. The same exact pool configuration works on one environment but not the other.
UPDATE 2 Clarified that this is not specifically an EJB/CMT issue, but a general JTA issue.
UPDATE 3 added info about JDBC autocommit. Confirmed that persistence.xml is correct.
It looks like this may be an issue with domain.xml, possibly a Glassfish bug.
In persistence.xml, I have
<jta-data-source>jdbc/TEST</jta-data-source>.
In domain.xml, I have
<jdbc-resource pool-name="TEST_POOL" description="" jndi-name="jdbc/TEST"></jdbc-resource>
But no corresponding <resource-ref ref="jdbc/TEST"> - either missing or misspelled. (I believe I ended up in that state by creating the JNDI datasource through the UI, realizing the name is wrong, then fixing the JNDI name in domain.xml jdbc-resource by hand but not fixing it in resource-ref.
In this case, my injected EntityManager still works but is not participating in JTA transactions. If I fix domain.xml, it works as expected.
You didn't wrap your Exception in an EJBException.
See http://docs.oracle.com/javaee/6/tutorial/doc/bnbpj.html
I am trying out simple JPA example on TomEE 4.0.0 and I am not able to get PersistenceContextType.EXTENDED working
If I make my session bean Stateless and leave PersistenceContextType then it works fine
#PersistenceContext(unitName = "xxx" )
private EntityManager entityManager;
If I keep my session bean as Stateless and then try to use this
#PersistenceContext(unitName = "xxx", type = PersistenceContextType.EXTENDED))
private EntityManager entityManager;
it gives me an error while deploying, which is perfectly fine and in line with expectation.
However now when I make my bean as #Stateful, then also it gives me an error
Managed ejbs are not capable of using EntityManagers with EXTENTED persistence. Convert your bean to a Stateful ejb or update the "java:comp/env/com.testwebservice.TestJPAService/entityManager" PersistenceContext reference to PersistenceContextType.TRANSACTION.
Can some one please help, I am really confused for this
#Stateful EXTENDED persistence contexts are tested in numerous ways in the TomEE build and also in the Java EE TCK.
Likely this is something else completely. We do our best to warn you when you make obvious mistakes as you note with the incorrect combination of #Stateless with PersistenceContextType.EXTENDED
Looking at the JNDI name which contains com.testwebservice.TestJPAService and given the fact it was #Stateless before it was changed to #Stateful, my guess is that this bean is also an #WebService which is illegal. #Stateful beans cannot be #WebService beans. Without a proper check to prevent this mistake, my guess is that the bean is actually being deployed twice; once as a #Stateful bean and once as an pojo #WebService. The pojo web service is the one causing the error.
In case that is the error, I've filed a JIRA for us to explicitly check that condition.