Oracle ESS (Enterprise Scheduling Service) is a nice feature that’s standard in Oracle SOA Suite 12c that alleviates the need to use Quartz or external software to run scheduled jobs (including invocations of services). Continuous Deployment can deliver many benefits but in order to get there, environment changes need to be automated as much as possible. AVIO has developed a Maven plugin that helps close the gap.

Details

Assuming you have some basic Oracle ESS knowledge, you know that ESS allows a lot of flexibility when defining job definitions, schedules, and job requests. With that flexibility comes the need to maintain consistent configurations across different environments. Oracle provides some of the tools (Java APIs, etc.) to get there but we need to close the gap between some code/configuration in source control and those APIs. The plugin’s primary job is to deploy/update web service based job definitions, schedules, requests, and WSM client policies in an idempotent fashion. Besides the CD need, it also allows some more flexibility with defining job schedules (e.g. working with an internal holiday calendar). The plugin does not focus on custom job definitions that live outside the native Oracle ESS hosting application.

How it works

The Maven plugin uses the ESS Java API that’s exposed via an EJB, which offers the richest way to interact with ESS. Another option is using WLST but that adds another layer abstraction that is not quite as powerful. The plugin high-level sequence works like this:

  • Maven (not plugin) compiles your “configuration” into Java byte code
  • Maven plugin fetches the “clean” objects that your factories create via reflection
  • Plugin converts objects to Oracle ESS Java API entities (and works around time zone issues, etc.)
  • Plugin updates/creates depending on what exists already
  • Plugin updates existing job requests with any new job parameters
  • Plugin attaches WSM client policies to the job definition if the service you are calling has policies – This functionality does use WLST. It also incorporates a self-deployed EJB service to work around a problem with how WSM recognizes deployed Oracle ESS jobs

Usage

Installation/environment setup

The plugin is currently available only via source. Visit github.com/avioconsulting/ess-maven-plugin and follow the instructions on how to build/install.

Whatever machine runs the Maven deployment will also need network access to the port of the Weblogic managed server that the Oracle ESS services are running on.

Create a project

You’ll need to create a directory in source control that will contain the “configuration” for your Oracle ESS artifacts. This will mainly take the form of lightweight Groovy classes that are automatically picked up by the plugin. In our experience, having this reside in source control with the SOA composite projects works well.

POM setup

Create a POM that references the plugin. Note the properties that need to be set. This plugin was developed assuming that Oracle ESS is deployed on the SOA cluster (and thus the property naming convention) but it should work fine with a separate ESS cluster.

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance" xsi_schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>MyGroup</groupId>
  <artifactId>MyApplication</artifactId>
  <version>1.0-SNAPSHOT</version>
  <description>Project for ESS schedules</description>
  <packaging>jar</packaging>

  <properties>
    <!-- This is the package that your classes inside this project live in -->
    <ess.config.package>com.myorg.ess</ess.config.package>
    <!-- Needs to match the time zone of the server you are deploying to -->
    <ess.server.timezone>America/Chicago</ess.server.timezone>
    <ess.plugin.version>1.0.3</ess.plugin.version>
    <soa.t3.url>t3://your_ess_server_hostname:soa_server1_port</soa.t3.url>
    <weblogic.user>username</weblogic.user>
    <weblogic.password>thepassword</weblogic.password>
  </properties>

  <dependencies>
    <!-- config class written in groovy -->
    <dependency>
      <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy-all</artifactId>
      <version>2.4.3</version>
      <scope>compile</scope>
    </dependency>
    <!-- We extend some of the plugin's provided classes -->
    <dependency>
      <groupId>com.avioconsulting</groupId>
      <artifactId>ess-maven-plugin</artifactId>
      <version>${ess.plugin.version}</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <compilerId>groovy-eclipse-compiler</compilerId>
        </configuration>
        <executions>
          <execution>
            <id>default-compile</id>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-eclipse-compiler</artifactId>
            <version>2.9.2-01</version>
          </dependency>
          <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-eclipse-batch</artifactId>
            <version>2.4.3-01</version>
          </dependency>
        </dependencies>
      </plugin>
      <plugin>
        <groupId>com.avioconsulting</groupId>
        <artifactId>ess-maven-plugin</artifactId>
        <version>${ess.plugin.version}</version>
        <executions>
          <execution>
            <id>deploy</id>
            <phase>pre-integration-test</phase>
            <goals>
  <!-- These are the 2 goals the plugin exposes, see the README for more information -->
              <goal>jobSchedule</goal>
              <goal>attachPolicies</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <!-- If you're using settings.xml for these, need to repeat them here, overriden values from settings.xml do not make it into the plugin for some reason -->
          <weblogicUser>${weblogic.user}</weblogicUser>
          <weblogicPassword>${weblogic.password}</weblogicPassword>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

This is enough to be able to locate your configuration within your project and deploy those configurations. Groovy isn’t required (you can use Java) but we used Groovy to express them in a concise manner.

Factories

Now all you have to do is populate your project with classes that define your job definitions, schedules, and requests. At a high level, all you need to do is create Groovy (or Java) classes in your project that implement the JobDefinitionFactory, ScheduleFactory, JobRequestFactory interfaces from the com.avioconsulting.ess.factories package in this plugin.

Job definitions

JobDefinition createJobDefinition() {
  new JobDefinition(jobType: JobDefinition.Types.SyncWebService,
                    description: 'the description4',
                    wsdlPath: '/some/wsdl6',
                    service: 'the_service_3',
                    port: 'the_port',
                    operation: 'the_operation',
                    message: '<message44/>',
                    name: 'SimplyBetter')
}

Schedules

RecurringSchedule createSchedule() {
  def holidays = [
    '2017-01-02',
    '2017-02-20',
    '2017-05-29',
    '2017-07-04',
    '2017-09-04',
    '2017-11-23',
    '2017-11-24',
    '2017-12-25'
  ].collect { dateStr -> new LocalDate(dateStr) }

  ScheduleBuilder.getWeeklySchedule name: 'SimplyBetter',
                                    displayName: 'the schedule',
                                    description: 'Weekly schedule on mondays',
                                    startDate: new LocalDate(2017, 1, 1),
                                    endDate: new LocalDate(2017, 12, 31),
                                    timeOfDay: new LocalTime(9, 15, 10),
                                    timeZone: DateTimeZone.forID('America/Chicago'),
                                    daysOfWeek: [DayOfWeek.MONDAY],
                                    holidays: holidays,
                                    alternateDirection: Direction.Backward
}

Job requests

JobRequest createJobRequest() {
  // remember the purpose of the job request is to tie together the schedule and job definition
  // the reason for calling createSchedule() and createJobDefinition() is to make this dependency
  // explicit and provide an easy way for the job request to be created and point to the name
  // of the schedule/job definition
  new JobRequest(description: 'recurring job',
                 schedule: createSchedule(),
                 jobDefinition: createJobDefinition())
}

Complete example

See GitHub

Executing

Executing the deployment is simple. Just run the Maven phase that you attached the plugin goals too. The above example would be executed by running mvn pre-integration-test. Once the goals execute, you can log in to Enterprise Manager and will see job requests scheduled and ready to run.

Details/source code

The plugin also has a helper for creating a “minutely” schedule or a monthly schedule. There are other options and information available along with the source code at github.com/avioconsulting/ess-maven-plugin