In part one of the series, we discussed using JDeveloper 12c and Maven as a build tool. Users know that there are problems with this configuration. The first Maven integration was a real bumpy experience. In later 12c versions (12.2.1.x) the Maven integration got better and better.

The current JDeveloper version 12.2.1.4 fixed most bugs. However, one thing broke with 12.2.1.4. You can’t run JUnit tests with Maven!

At the end of part one, we had a sample workspace using Maven as the built tool. This workspace contains three projects:

The one project we are interested in for this part is the BMAModelTest project. We use it to run JUnit test for the ADF model in project BMAModel.

As the result of runnimg the JUnit tests we get this error:

In this part, I firstly show a workaround and secondly how to implement it to fix the problem.

Workaround

I did many tests to find a solution for the JDNI lookup failure we see in the error above. I have an SR running with support for this too. Oracle Support is on this bug for months, and nothing happens 🙁

A big ADF project is running out on time waiting for this bug to be fixed before we can migrate to JDev 12.2.1.4. Migration is mandatory. We either can skip the tests, or we can try to work around the problem.

Finally, I found that creating a RootApplicationModule using the Configuration class is the problem. The Configuration is used to create the application module and to connect it to the database. Somehow, connecting the application module to the database is broken. The JNDI lookup doesn’t work either because the data isn’t found or the credentials are not found or valid. The same works in JDeveloper version before 12.2.1.4.

We use the JDBC Datasource to connect the application module to the DB. In 12c this is the standard way to do it. However, there is another way to attach a DB connection to an application module.

Instead of using the JDBC Data Source, we can use a JDBC URL. The disadvantage is that you have to specify a user name/password for the URL, and the URL has the target server (and other DB stuff) wired into it. A JDBC URL makes it hard to use different DBs for different environments, e.g., development, qs or production.

Creating a RootApplicationModule by Hand 

We can’t use the Configuration.createRootApplicationModule method the create the RootApplicationModule as this throws the error. We can create the RootApplicationModule using the home interface of the application module. Using EJBs, this is the normal way to get an application module. 

The class InitialContext is used to lookup the Application module and then create a RootApplicationModule. Here is the needed code:

InitialContext ctx = new InitialContext();
Object amHome = ctx.lookup("de.hahn.blog.mavenapp.model.adfbc.facade.BMAAppModule");
ApplicationModuleHome ahome = (ApplicationModuleHome) amHome;
// create ApplicationModule via ApplicationModuleHome
ApplicationModule applicationModule = ahome.create();
// connect ApplicationModule to DB
applicationModule.getTransaction().connect(jdbcurl, jdbcuser, jdbcpwd);

The InitialContext is used to look up the definition of the ApplicationModule. The same definition is used by the Configuration class. Once we found the home interface we can create the instance of the application module. This instance is then connected to the DB providing the JDBC URL, user name and password.

Implementing the Workaround

We change the fixture created by the framework to create the application module in the way outlined above:

package de.hahn.blog.mavenapp.model.adfbc.facade.applicationModule;

import java.util.Hashtable;

import javax.naming.InitialContext;

import oracle.adf.share.ADFContext;
import oracle.adf.share.logging.ADFLogger;

import oracle.jbo.ApplicationModule;
import oracle.jbo.ApplicationModuleHome;
import oracle.jbo.JboContext;

public class BMAAppModuleAMFixture {
    private static ADFLogger log = ADFLogger.createADFLogger(BMAAppModuleAMFixture.class);
    private static BMAAppModuleAMFixture fixture1 = new BMAAppModuleAMFixture();
    private ApplicationModule _am;

    private BMAAppModuleAMFixture() {
        ADFContext adfContext = null;
        try {
            log.info("Create AM for test run...");
            // create new context
            adfContext = ADFContext.initADFContext(null, null, null, null);

            // setup environment for lookup
            Hashtable ht = new Hashtable(4);
            ht.put(JboContext.INITIAL_CONTEXT_FACTORY, JboContext.JBO_CONTEXT_FACTORY);
            ht.put(JboContext.DEPLOY_PLATFORM, JboContext.PLATFORM_LOCAL);
            InitialContext ctx = new InitialContext(ht);

            // Definition of the application module (same as we use for createRootApplicationModule)
            String def = "de.hahn.blog.mavenapp.model.adfbc.facade.BMAAppModule";
            Object amHome = ctx.lookup(def);
            ApplicationModuleHome ahome = (ApplicationModuleHome) amHome;
            // ApplicationModule anlegen über das ApplicationModuleHome
            ApplicationModule applicationModule = ahome.create();
            log.info("am:" + applicationModule);
            // ApplicationModule mit der DB verbinden
            String jdbcurl = "jdbc:oracle:thin:@//localhost:1521/XEPDB1";
            applicationModule.getTransaction().connect(jdbcurl, "hr", "hr");
            log.info("...created am=" + applicationModule);
            _am = applicationModule;
        } catch (Exception e) {
            log.severe(e);
            e.printStackTrace();
        } finally {
            ADFContext.resetADFContext(adfContext);
        }
    }

    public void release() throws Exception {
        try {
            if (null != _am) {
                log.info("Disconnrect am=" + _am + " name=" + _am.getDefFullName());
                // disconnect am from transaction
                _am.getTransaction().disconnect();
            }
            _am = null;
        } catch (Exception e) {
            log.warning("Couldn't release fixture!", e);
        }
    }

    public void setUp() {
    }

    public void tearDown() {
    }

    public static BMAAppModuleAMFixture getInstance() {
        return fixture1;
    }

    public ApplicationModule getApplicationModule() {
        return _am;
    }
}

Using the new fixture we now can run the tests from the Maven ‘test’ goal 🙂

Or you can run them inside JDeveloper using the JUnit Runner:

This outlines the workaround to run JUnit tests with Maven. All of you running into the same problem can use this as a starting point. For real world projects more things need to be done. Connecting to different databases, depending on the model project is one of them.

In part three of this series, I’ll point out some problems with this workaround and show how to fix them too. We will discuss how to make general usage of this workaround for bigger projects (or multi-project builds), how to add some logging to show what is going on when running a test and how to pass different JDBC connection data when needed.

Timo Hahn