2

I have a JPA application that has specified JTA transactions in persistence.xml. For whatever reason, I have found that when using JTA, you MUST specify a JNDI data source within persistence.xml as well. This is fine, unless you are trying to go integration testing outside a container and JNDI is not available.

My questions are:

a) is there anyway to inject a jdbc datasource into my JTA transaction manager? b) if not, how do a handle a JNDI lookup during integration testing?

Edit: The error I get when firing up the integration test is:

 Caused by: org.springframework.......DataSourceLookupFailureException:
 Failed to look up JNDI DataSource with name 'java:comp/env/jdbc/myAppDataSource';
 nested exception is javax.naming.NoInitialContextException: Need to specify
 class name in environment or system property, or as an applet parameter,
 or in an application resource file:  java.naming.factory.initial
Dave
  • 21,524
  • 28
  • 141
  • 221

2 Answers2

1

I ran into a similar problem myself. I'm using Spring with JPA and specifying my database as a JNDI name. Eventually I decided to just mock out JNDI using Mockito. This was pretty easy. Here is how I did it:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring-config.xml"})
public class MyTestClass {

    private static final InitialContextFactoryBuilder contextFactoryBuilder = mock(InitialContextFactoryBuilder.class);
    private static final InitialContextFactory contextFactory = mock(InitialContextFactory.class);
    private static final Context context = mock(Context.class);
    private static final NameParser parser = mock(NameParser.class);
    private static final Name dbName = mock(Name.class);
    // This is the Datasource implementation from the H2 database
    private static final JdbcDataSource temporaryDbForTesting = new JdbcDataSource();

    @BeforeClass
    public static void setupMockJndi() throws NamingException {
        NamingManager.setInitialContextFactoryBuilder(contextFactoryBuilder);
        when(contextFactoryBuilder.createInitialContextFactory(any(Hashtable.class))).thenReturn(contextFactory);
        when(contextFactory.getInitialContext(any(Hashtable.class))).thenReturn(context);
        when(context.getNameParser(any(String.class))).thenReturn(parser);
        when(parser.parse("GatewayDbDataSource")).thenReturn(dbName);
        when(context.lookup(dbName)).thenReturn(temporaryDbForTesting);

        temporaryDbForTesting.setURL("jdbc:h2:~/test2");
        temporaryDbForTesting.setUser("sa");
        temporaryDbForTesting.setPassword("");
    }

    @Autowired
    private org.zzz.DomainObject toTest;

    @Test
    public void testPasswordChecking() {
        assertNotNull("There wasn't an object to test. Spring is broken!", toTest);
        // ... assertions and such ...
    }

}
Spina
  • 8,986
  • 7
  • 37
  • 36
  • I ended up with very similar solution, after noticing that SimpleContextFactoryBuilder does not support queries for Names - making it unusable for my use case of loading the datasource through persistence.xml – Jukka Dahlbom Jul 23 '13 at 20:19
1

I didn't experiment this myself but it is doable with a standalone transaction manager and a XA compliant datasource. With Spring 2.5, most samples use JOTM and the JotmFactoryBean and the XAPool. But these are non longer supported in Spring 3.0 and Atomikos appears to be the "replacement" (it provides a standalone transaction manager and a XA datasource). I've added some configuration samples below.

The alternative would be to run in-container tests (using an embedded Java EE container e.g. GlassFish v3 Embedded or an API like Cargo).

Resources

Community
  • 1
  • 1
Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • The link to JotmFactoryBean is broken. In fact, that class is missing from Spring-tx 3.0.1. I am trying to use Atomikos as the standalone transaction manager. However, Spring (or JPA/Hibernate) is continuing to try and look for the JTA data source from jndi that was specified in persistence.xml. – Dave Jun 10 '10 at 01:15
  • @HDave Indeed, I didn't noticed that but the JOTM stuff has been removed from Spring 3.x (for a good reason I guess). I've updated my answer. Regarding JPA, I think that you'll need the trick described in this [blog post](http://erich.soomsam.net/2007/04/24/spring-jpa-and-jta-with-hibernate-and-jotm/) (that I understand better now). – Pascal Thivent Jun 10 '10 at 02:16
  • I've read the resources extremely carefully. I see that the important thing is to "tell" Hibernate about Atomikos via hibernate.transaction.factory_class and manager_lookup_class -- GREAT!. But in all cases, I see that the Entity Manager configuration is supplying a data source. According to the Spring javadocs on dataSource "In JPA speak, a DataSource passed in here will be used as "nonJtaDataSource" on the PersistenceUnitInfo passed to the PersistenceProvider, overriding data source configuration in persistence.xml (if any)." It seems that the EM is in RESOURCE_LOCAL mode!! – Dave Jun 10 '10 at 03:23
  • @HDave I'll reread this more carefully later but I thought this was the point of using the `JtaPersistenceUnitPostProcessor` from http://erich.soomsam.net/2007/04/24/spring-jpa-and-jta-with-hibernate-and-jotm/. – Pascal Thivent Jun 10 '10 at 03:41
  • I've implemented the JtaPersistenceUnitPostProcessor as outlined in that document and in combination with the hibernate.transaction.factory_class and manager_lookup_class it appears to be working. Who knew just running JPA/JTA outside a container was so hard!!! – Dave Jun 10 '10 at 05:07