Oracle ADF and Gradle integration – part#3: Dependency Management

Oracle ADF and Gradle integration – part#3: Dependency Management

Library dependency management is one of  the most missing tools in Oracle JDeveloper 11g. It is true that Maven support has been added in JDeveloper 12c. Nevertheless, in the older projects you may want to use Gralde integration described in this article. This approach also works with JDeveloper 12c.

 

I. Overview

This tutorial is intended for both JDeveloper 11g and 12c. There is only one difference between them, which I will discuss when we get to it.

From the tutorial you will learn how to:

  • write custom Groovy/Java helper class and use it in Gradle script;
  • design custom dependency model using Gradle dependency management;
  • integrate Gradle dependency management with Oracle ADF application.

 

But before we start

If you have not read the following articles yet, I will recommend that you do so because it will be much easier to understand you the tutorial.

 

The Idea

Before the release of JDeveloper 12c, the standard way to add library to Oracle ADF application was by creating and attaching *.library  file into the project. The file contains entries pointed to jar files used in the project. Here is an example:

<?xml version = '1.0' encoding = 'UTF-8'?>
<JLibraryNode nselem="JLibraryNode" class="oracle.jdeveloper.library.JLibraryNode" deployedByDefault="true" xmlns="http://xmlns.oracle.com/jdeveloper/110000jlibrarynode">
   <classPath>
      <entries>
         <Item path="jackson-core-2.9.4.jar"  jar-entry=""/>
         <Item path="guava-19.0.jar"  jar-entry=""/>
         <Item path="jackson-databind-2.9.4.jar"  jar-entry=""/>
         <Item path="jackson-annotations-2.9.0.jar"  jar-entry=""/>
      </entries>
   </classPath>
   <docPath/>
   <locked>false</locked>
   <name>Sample_library</name>
   <sourcePath/>
</JLibraryNode>

 

Let’s think what would happened if we could generate custom library file and attach it into project. It sounds good, doesn’t it? I will tell you more – it works as it should. Attaching a library file to the project is relatively easy so, most of the tutorial is about how to generate the right *.library file in the right place.

In this tutorial, we will create two library files for both the Model and ViewController projects. Files will be located in adf-gradle/build/libraries/ADFGradle_model and adf-gradle/build/libraries/ADFGradle_vc directories. They will be generated based on previously downloaded jar files. Jars will be located in the same folders as library files and will be downloaded by newly created Gradle task. The task will use an utility class written in groovy.  This is what the idea from the end looks like.

 

Sample Application

For the purposes of the tutorial I prepared an exemplary application which you can download from here.  The application is compatible with JDeveloper 11.1.1.7 but it is also possible to open it in JDeveloper 12c. It has already been integrated with Gradle as I explained it in this tutorial.

.
├── adf-gradle
│ └── build.gradle
├── GradleDependencyManagement.jws
├── Model
├── src
└── ViewController
    ├── public_html
    ├── src
    │ └── com
    │     └── bettercoding
    │         └── gradle
    │             └── view
    │                 └── bean
    │                     └── SampleBean.java
    └── ViewController.jpr

 

As you probably noticed,  the ViewController project contains SampleBean.java class. The class imports external packages like com.fasterxml.jackson.core.type.TypeReference and it depends on jackson-databind library (which also depends on two other libraries: jackson-core  and jackson-annotations). The project does not compile because of missing dependencies. Your goal is to make is to make it work.

 

II. Gradle Task: downloadDependencies

In this part of the tutorial you will see how to write a Gradle script which allows you to:

  • configure dependencies for both Model and ViewController projects;
  • download all previously configured libraries (with theirs dependencies) into the right directories;
  • create ADF Library files for both Model and ViewController projects;

 

Utility class: AdfGradleUtils.groovy

Although there is a possibility to write whole code directly in build.gradle file I decided to put it into separated groovy class. We can advantage the fact that every class placed in the adf-gradle/buildSrc/src/main/groovy directory is automatically compiled and ready to use in build.gradle script. By the way, it is also possible to write Java class using the following location adf-gradle/buildSrc/src/main/java.

At  this moment we will prepare a mocked utility class adf-gradle/buildSrc/src/main/groovy/AdfGradleUtils.groovy  with the following content:

import org.gradle.api.Project
import org.gradle.api.internal.artifacts.configurations.DefaultConfiguration


class AdfGradleUtils {
    static void processDependencies(Project project, String librariesDir, DefaultConfiguration configuration, String jdevVersion) {
        println("processDependencies: " + configuration.name)
    }

    private static generateAdfLibraryFile(Project project, String libraryDir, String libraryName, String jdevVersion) {
        println("generateAdfLibraryFile: " + libraryDir + "/" + libraryName)
    }
}

 

The class contains two methods. The first one is responsible for downloading all dependencies defined in configuration parameter. The second method is responsible for creating ADF library file based on previously downloaded libraries. The methods are not finished yet. We will finish it later.

 

Integration: build.gradle

Heaving a separate utility class makes the integration process much more clearer. At first we have to define repositories and both configurations for our application(for Model and ViewController projects). In this case we will use Central Maven Repository as default source of artifacts.

group 'com.bettercoding'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

configurations {
    ADFGradle_model
    ADFGradle_vc
}

...

 

Then we have to define project properties which helps us to stick to canons:

  • librariesDir – property defines a target directory for libraries downloaded by downloadDependencies  task.
  • jdevVersion – property will be used to select the right ADF library file template(11g or 12c).
...

ext {
    middlewareHome = project.hasProperty("middlewareHome") ? project.property("middlewareHome") : null
    ojDeployExec = middlewareHome + "/Oracle_Home/jdeveloper/jdev/bin/ojdeploy"
    adfWorkspace = ".."
    adfWorkspaceDeployDir = "${adfWorkspace}/deploy"
    gradleTargetDir = "./build"
    ojDeployBuildfile = "./ojdeploy-buildfile.xml"
    ojDeployAppWorkspaceDir = new File(adfWorkspace).getAbsolutePath()
    librariesDir = gradleTargetDir + "/libraries"
    jdevVersion = "11g"
}

...

 

The last thing that’s left is to define downloadDependencies  task and set it as dependency of assemble task.

...

task downloadDependencies {
    group "oracle-adf"
    description "Resolve and download ADF dependencies"
    doLast {
        for (config in configurations) {
            delete {fileTree(project.ext.librariesDir)}
            AdfGradleUtils.processDependencies(project, project.ext.librariesDir, config, project.ext.jdevVersion)
        }
    }
}

task assemble {
    group "oracle-adf"
    description "Build ADF artficats"
    mustRunAfter clean, checkAdfEnvironment, downloadDependencies
    dependsOn clean, checkAdfEnvironment, downloadDependencies
...

 

The final result of build.gradle file you can see on the listing below.

group 'com.bettercoding'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

configurations {
    ADFGradle_model
    ADFGradle_vc
}

ext {
    middlewareHome = project.hasProperty("middlewareHome") ? project.property("middlewareHome") : null
    ojDeployExec = middlewareHome + "/Oracle_Home/jdeveloper/jdev/bin/ojdeploy"
    adfWorkspace = ".."
    adfWorkspaceDeployDir = "${adfWorkspace}/deploy"
    gradleTargetDir = "./build"
    ojDeployBuildfile = "./ojdeploy-buildfile.xml"
    ojDeployAppWorkspaceDir = new File(adfWorkspace).getAbsolutePath()
    librariesDir = gradleTargetDir + "/libraries"
    jdevVersion = "11g"
}

task  checkAdfEnvironment {
    group "oracle-adf"
    description "Check if ADF environment configured correctly"
    doLast {
        if (project.ext.middlewareHome == null ) {
            throw new GradleException("Missing project property [middlewareHome]. " +
                    "Add -PmiddlewareHome=<MIDDLEWARE_HOME> switch to gradle invomation")
        }
    }
}

task clean {
    group "oracle-adf"
    description "Clean ADF workspace"
    mustRunAfter checkAdfEnvironment
    dependsOn checkAdfEnvironment
    doLast {
        println "Cleaning project ${project.name}"

        println "Deleting ${project.ext.adfWorkspaceDeployDir}"
        delete fileTree(project.ext.adfWorkspaceDeployDir)

        println "Deleting ${project.ext.gradleTargetDir}"
        delete fileTree(project.ext.gradleTargetDir)
    }
}

task downloadDependencies {
    group "oracle-adf"
    description "Resolve and download ADF dependencies"
    doLast {
        for (config in configurations) {
            delete {fileTree(project.ext.librariesDir)}
            AdfGradleUtils.processDependencies(project, project.ext.librariesDir, config, project.ext.jdevVersion)
        }
    }
}

task assemble {
    group "oracle-adf"
    description "Build ADF artficats"
    mustRunAfter clean, checkAdfEnvironment, downloadDependencies
    dependsOn clean, checkAdfEnvironment, downloadDependencies
    doLast {
        println "Building using OJDeploy:"
        //Build
        exec {
            commandLine project.ext.ojDeployExec, '-buildfile', project.ext.ojDeployBuildfile, '-define', "app.workspace.dir=${project.ext.ojDeployAppWorkspaceDir}"
        }
    }
}

 

Time for test

Run ./gradlew downloadDependencies and check if everything went OK.

better-coding@bc-vbox:~/ADFGradle-dependency-management-final/adf-gradle$ ./gradlew downloadDependencies
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes
:buildSrc:jar
:buildSrc:assemble
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build
:downloadDependencies
processDependencies: ADFGradle_model
processDependencies: ADFGradle_vc

BUILD SUCCESSFUL

Total time: 3.442 secs
better-coding@bc-vbox:~/ADFGradle-dependency-management-final/adf-gradle$

 

Implementation: AdfGradleUtils.groovy

It is time to finish the AdfGradleUtils.groovy class, but before we do this we have to prepare some xml files with ADF library templates. It makes our code much clearer than without it.

 

ADF Library template

Prepare files like below. You can add one or both pairs of files. There is a small difference between them that you can see when comparing individual files.

For ADF 11g – you have to add template-library-11g.xml and template-entry-11g.xml files with the following content:

<?xml version = '1.0' encoding = 'UTF-8'?>
<JLibraryNode nselem="JLibraryNode" class="oracle.jdeveloper.library.JLibraryNode" deployedByDefault="true" xmlns="http://xmlns.oracle.com/jdeveloper/110000jlibrarynode">
   <classPath>
      <entries>
         <Item protocol="jar" path=""/>
<!-- ENTRIES -->
      </entries>
   </classPath>
   <docPath/>
   <locked>false</locked>
   <name><!-- LIBRARY_NAME --></name>
   <sourcePath/>
</JLibraryNode>

 

         <Item path="<!-- ENTRY_NAME -->"  jar-entry=""/>

 

For ADF 12c – you have to add template-library-12c.xml and template-entry-12c.xml files with the following content:

<?xml version="1.0" encoding="UTF-8" ?>
<library id="oracle..<!-- LIBRARY_NAME -->" name="<!-- LIBRARY_NAME -->" deployed="true" xmlns="http://xmlns.oracle.com/jdeveloper/1013/manifest-libraries">
   <description/>
      <classpath/>
<!-- ENTRIES -->
</library>

 

      <classpath><!-- ENTRY_NAME --></classpath>

 

Finishing implementation

Edit AdfGradleUtils.groovy class and finish the processDependencies method with the following code:

    static void processDependencies(Project project, String librariesDir, DefaultConfiguration configuration, String jdevVersion) {
        println("processDependencies: " + configuration.name)

        String destDirPath = librariesDir + "/" + configuration.name
        File destDir = new File(destDirPath)

        destDir.mkdirs()

        project.copy {
            from configuration
            into destDirPath
        }

        generateAdfLibraryFile(project, destDirPath, configuration.name, jdevVersion)
    }

 

Let’s think for a moment what processDependencies method actually do. It prepares the right directory structure and copies all jars (with dependencies) into the directory. At The end the method calls generateAdfLibraryFile.

The generateAdfLibraryFile  method is responsible for generating ADF Library file with entries pointed to previously downloaded jars. The idea of the method is very simple – just list all jar files from the directory and add it into library. The body of the  method may looks like below.

private static generateAdfLibraryFile(Project project, String libraryDir, String libraryName, String jdevVersion) {
        println("generateAdfLibraryFile: " + libraryDir + "/" + libraryName)

        String entryTemplate = new File("buildSrc/src/main/resources/template-entry-${jdevVersion}.xml").getText("UTF-8")
        String entries = ""
        project.fileTree(libraryDir).each {
            if (it.name.toLowerCase().endsWith(".jar")) {
                entries += entryTemplate.replace("<!-- ENTRY_NAME -->", it.name) + "\n";
            }
        }

        String libraryTemplate = new File("buildSrc/src/main/resources/template-library-${jdevVersion}.xml").getText("UTF-8")
        libraryTemplate = libraryTemplate.replace("<!-- ENTRIES -->", entries)
        libraryTemplate = libraryTemplate.replace("<!-- LIBRARY_NAME -->", libraryName)

        String libraryFilename = "${libraryName}.library"
        new File(libraryDir, libraryFilename).write(libraryTemplate, "UTF-8")
    }

 

Below you can see the final version of AdfGradleUtils.groovy class.

import org.gradle.api.Project
import org.gradle.api.internal.artifacts.configurations.DefaultConfiguration


class AdfGradleUtils {
    static void processDependencies(Project project, String librariesDir, DefaultConfiguration configuration, String jdevVersion) {
        println("processDependencies: " + configuration.name)

        String destDirPath = librariesDir + "/" + configuration.name
        File destDir = new File(destDirPath)

        destDir.mkdirs()

        project.copy {
            from configuration
            into destDirPath
        }

        generateAdfLibraryFile(project, destDirPath, configuration.name, jdevVersion)
    }

    private static generateAdfLibraryFile(Project project, String libraryDir, String libraryName, String jdevVersion) {
        println("generateAdfLibraryFile: " + libraryDir + "/" + libraryName)

        String entryTemplate = new File("buildSrc/src/main/resources/template-entry-${jdevVersion}.xml").getText("UTF-8")
        String entries = ""
        project.fileTree(libraryDir).each {
            if (it.name.toLowerCase().endsWith(".jar")) {
                entries += entryTemplate.replace("<!-- ENTRY_NAME -->", it.name) + "\n";
            }
        }

        String libraryTemplate = new File("buildSrc/src/main/resources/template-library-${jdevVersion}.xml").getText("UTF-8")
        libraryTemplate = libraryTemplate.replace("<!-- ENTRIES -->", entries)
        libraryTemplate = libraryTemplate.replace("<!-- LIBRARY_NAME -->", libraryName)

        String libraryFilename = "${libraryName}.library"
        new File(libraryDir, libraryFilename).write(libraryTemplate, "UTF-8")
    }
}

 

Time for test

Run ./gradlew downloadDependencies and check if everything went OK. A this moment both empty ADF libraries should be generated.

better-coding@bc-vbox:~/jdeveloper/mywork/ADFGradle-dependency-management-final/GradleDependencyManagement/adf-gradle$ ./gradlew downloadDependencies
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
:downloadDependencies
processDependencies: ADFGradle_model
generateAdfLibraryFile: ./build/libraries/ADFGradle_model/ADFGradle_model
processDependencies: ADFGradle_vc
generateAdfLibraryFile: ./build/libraries/ADFGradle_vc/ADFGradle_vc

BUILD SUCCESSFUL

Total time: 1.174 secs
better-coding@bc-vbox:~/jdeveloper/mywork/ADFGradle-dependency-management-final/GradleDependencyManagement/adf-gradle$ 

 

III. Configure ADF project

The last thing we have to do is to attach generated ADF library files into ADF Application. Run JDeveloper and open Project Properties menu. Then go to  Libraries and Classpath , click Add Library , click Load  and choose the directory with generated ADFGradle_vc  library. Then select the library.

 

Repeat above for Model project and try to rebuild the project. You should got a compilation error, because we have not defined any library yet.

 

To add the required libraries put the following lines to build.gradle  file:

...
configurations {
    ADFGradle_model
    ADFGradle_vc
}

dependencies {
    ADFGradle_model(group: 'joda-time', name: 'joda-time', version: '2.9.9')
    ADFGradle_vc(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.4')
    ADFGradle_vc(group: 'com.google.guava', name: 'guava', version: '19.0')
}


ext {
...

 

and refresh dependencies by running ./gradlew downloadDependencies command.

better-coding@bc-vbox:~/jdeveloper/mywork/ADFGradle-dependency-management-final/GradleDependencyManagement/adf-gradle$ ./gradlew downloadDependencies
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
:downloadDependencies
processDependencies: ADFGradle_model
generateAdfLibraryFile: ./build/libraries/ADFGradle_model/ADFGradle_model
processDependencies: ADFGradle_vc
Download https://repo1.maven.org/maven2/com/google/guava/guava/19.0/guava-19.0.pom
Download https://repo1.maven.org/maven2/com/google/guava/guava-parent/19.0/guava-parent-19.0.pom
Download https://repo1.maven.org/maven2/com/google/guava/guava/19.0/guava-19.0.jar
generateAdfLibraryFile: ./build/libraries/ADFGradle_vc/ADFGradle_vc

BUILD SUCCESSFUL

Total time: 4.12 secs
better-coding@bc-vbox:~/jdeveloper/mywork/ADFGradle-dependency-management-final/GradleDependencyManagement/adf-gradle$ 

 

The final test

Now it is time to rebuild your project again.

 

Everything went perfect! . You can download the final version of application from here.

 


What’s next?

Please leave me a comment what do you think about this tutorial and what do you want to read about in the next tutorial

If this tutorial was interesting to you can help me to reach a larger group of receivers by sharing this post.

Thank you!

 

 

Leave a Reply

avatar
  Subscribe  
Notify of
Close Menu