Building a simple microservice using Spring Boot

In this short post, I will show how to build a simple JPA Microservice using Spring Boot. Spring Boot makes it easy to create stand-alone based applications that you can run and need very little Spring configuration as we will see in this short tutorial.

springboot

For an explanation about microservices, read this article of Martin Fowler.

As I was saying we are going to use Spring Boot. First start of with a simple java maven enabled project in the IDE of your choice. We will first start with the .pom file to get all the Spring dependencies right. I am going to build a simple ReferenceDataService microservice which can deliver some simple reference data such as a list of countries. Let take a look at the pom file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 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>

    <artifactId>ReferenceDataService</artifactId>
    <groupId>nl.redrock</groupId>
    <packaging>jar</packaging>
    <name>ReferenceData Microservice</name>
    <description>ReferenceData Service</description>
    <version>1.0</version>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>
    
	
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Oracle JDBC driver -->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc7</artifactId>
            <version>7.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

As you can see, we the 1.5.2.RELEASE as a basis. We then have 4 dependencies. We need spring-boot-starter-web for libraries to creating the rest service, we need spring-boot-starter-data-jpa for the jpa capability, we need spring-boot-starter-test for testing capabilities and last an ojdbc.jar because the data is located in a Oracle database. I am also adding the spring-boot-maven-plugin to be able to run it from maven using Tomcat.

Let start of with the entry point….the application. This is the starting point of our simple service. It will look like this:

package nl.redrock.referencedataservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ReferenceDataApplication {

    public static void main(String[] args) {
        SpringApplication.run(ReferenceDataApplication.class, args);
    }
}

As you can see, it is very simple. Use the @SpringBootApplication annotation to tag it as a Spring Boot application and that is it. Next we want a class which can retrieve data from database. Spring has a bunch helpful classes and templates for you to help with this. I already have a pre-filled oracle database running with 1 table called country. It has 3 columns….ID, CODE and NAME.

database

So as a first step we will create a Country class which maps to the table.

package nl.redrock.referencedataservice.data;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Country {
    
    @Id
    private int id;
    private String code;
    private String name;

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the code
     */
    public String getCode() {
        return code;
    }

    /**
     * @param code the code to set
     */
    public void setCode(String code) {
        this.code = code;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return String.format("Country[id=%d, code='%s', name='%s']",
                id, code, name);
    }
}

As you can see this is also a very simple POJO with the 3 attributes mapping to the 3 columns with getter and setters. The annotation which does al the magic here is @Entity. This tells spring that it can be used for Object Relational Mapping. Next we we will create a class which will fetch the data.

package nl.redrock.referencedataservice.repository;

import nl.redrock.referencedataservice.data.Country;
import org.springframework.data.repository.CrudRepository;

public interface CountryRepository extends CrudRepository<Country, Long> {

    Country findById(int id);
}

And again….not much coding here. Just a simple interface which extends Spring’s CRUDRepository. We define just 1 extra interface for retrieving a country by its id. And basically that is it. We just have 1 thing left to do, and that is telling Spring which database to connect to. This is easily done by adding an application.properties to you classpath with all the settings in so it is automatically picked up by Spring.

#port to run apache on
server.port=8888

# Oracle settings
spring.datasource.url=jdbc:oracle:thin:@private.eu-west-1.compute.amazonaws.com:1521:xe
spring.datasource.username=SECRET
spring.datasource.password=SECRET
spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver

#set sql level to debug to see all the sql statements
logging.level.org.hibernate.SQL=debug
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n

The server.port is a setting which you can use to adjust the port tomcat runs on. Next up are the oracle database connection settings. And last of all some logging tweaking. A last thing to do is to make a unit test to see if it al works.

package nl.redrock.referencedataservice.repository;

import junit.framework.TestCase;
import nl.redrock.referencedataservice.data.Country;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
public class CountryRepositoryTest extends TestCase {
    
    @Autowired
    CountryRepository countryRepository;
    
    @Test
    public void testCountryRepository(){
        Country c = this.countryRepository.findById(1);
        assertTrue(c != null);
        assertTrue(c.getCode().equals("ac"));
        assertTrue(c.getName().equals("Ascension Island"));
    }
}

The things to look for here are the @DataJpaTest annotation which tells Spring it is a JPA test. The @AutoConfigureTestDatabase(replace=Replace.NONE) annotation tells Spring to not replace the application default DataSource.

Run the test:

UT

As you can see, we can fetch data from the database with minimal coding.

Now for the service part. Spring also has easy ways to accommodate this using the @RestController annotation.

package nl.redrock.referencedataservice.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import nl.redrock.referencedataservice.data.Country;
import nl.redrock.referencedataservice.repository.CountryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/referencedataservice/countries")
public class ReferenceDataController {
    
    private final static Logger LOGGER = Logger.getLogger(ReferenceDataController.class.getName());
    
    @Autowired
    CountryRepository countryRepository;


    @RequestMapping("/{id}")
    public Country getCountry(@PathVariable int id) {
        Country result;
        LOGGER.log(Level.INFO, "Getting country with id " + id);
        result = countryRepository.findById(id);
        return result;
    }

    @RequestMapping(method = RequestMethod.GET)
    List<Country> getCountries() {
        List<Country> result;
        LOGGER.log(Level.INFO, "Getting all countries");
        result = new ArrayList();
        Iterable<Country> countryList = countryRepository.findAll();
        for (Country country : countryList) {
            result.add(country);
        }
        return result;
    }
}

As you can see we implement 2 operations. 1 to get all the countries and 1 to get a specific country by its id. We use @Autowired to inject the countryRepository. And now for the proof of the pudding. Run mvn clean spring-boot:run and watch maven spin up a Tomcat instance with the referencedataservice application deployed on it. Open up the browser and call:

http://localhost:8888/referencedataservice/countries

countriesResult

Now call http://localhost:8888/referencedataservice/countries/160 to get a specific country

countryResult

As you can see Spring makes it very easy to create rest services with minimal coding. If you want to look into some more advanced microservice features Spring has to offer, have a look here and here to see how you can use microservices in conjunction with Netflixs Eureka server.

SOA Suite 12C: Add version information to your ServiceBus projects using custom maven plugin

As you have seen in my previous posts it is possible to build your SB and SOA components using Maven. See here. One of the issues we encountered is that building ServiceBus projects supports some very basic maven stuff. For example the description property in the maven pom file is not mapped to the description field of a service bus project which would have been nice as this is the only extra field which we can use to put some extra information….for example version info!

For SOA composites you are able to input some versioning info by updating the composite.xml using a Google maven plugin. Just add this plugin to build:

<!--Needed to replace the revision in the composite.xml due to bug (20553998) which causes not to update the revision correctly -->
<plugin>
	<groupId>com.google.code.maven-replacer-plugin</groupId>
	<artifactId>replacer</artifactId>
	<version>1.5.3</version>
	<executions>
		<execution>
			<phase>initialize</phase>
			<goals>
				<goal>replace</goal>
			</goals>                   
		</execution>
	</executions>
	<configuration>
		<ignoreMissingFile>true</ignoreMissingFile>
		<file>${scac.input}</file>
		<xpath>//composite/@revision</xpath>
		<token>^.*$</token>
		<value>${composite.revision}</value>
	</configuration>
</plugin>

which result into:
versioningSoa

For SB projects there is no such thing. How can we then see which version we have?! Well the only field which we can use is the description field. The only problem is that you can not update this using Maven. The things you have to:

  • Unzip the sbconfig.sbar
  • Update the _projectdata.LocationData file which holds a proj:description tag
  • Zip the sbconfig.sbar again

Not too difficult at all. You probably can do this by using Ant but that is so 2001! We have Maven now so why not write a custom Maven plugin which does this all for you?! Well I won’t bother you with the Java details but you can download the plugin jar here!

Just install it into your Maven repository by running:

    mvn install:install-file -Dfile=version-information-plugin-1.0.jar -DgroupId=nl.redrock.maven.plugins.servicebus -DartifactId=version-information-plugin -Dversion=1.0 -Dpackaging=jar

Now that you have the plugin installed you can wire it to the package phase of your service bus project by adding the plugin to your service bus pom file. Mine looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.oracle.servicebus</groupId>
    <artifactId>sbar-project-common</artifactId>
    <version>12.1.3-0-0</version>
  </parent>
  <groupId>nl.redrock</groupId>
  <artifactId>ConversionRateService</artifactId>
  <version>1.1.2.5</version>
  <packaging>sbar</packaging>
  <description></description>
  <build>
    <plugins>
     <plugin>
        <groupId>nl.redrock.maven.plugins.servicebus</groupId>
        <artifactId>version-information-plugin</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>version</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <description>${project.version}</description>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

This will now call the plugin after the normal packaging has been completed. In the configuration you can now set the description. In my case I fill it with the versioning info of the component itself. Now just build your service bus project using Maven:

mavenbuild

and deploy and go to the sbconsole and voila:

sbconsole

OFM 12C: Running WLST scripts in your build pipeline using the weblogic-maven-plugin

When you are building and deploying servicebus or soa composites to the server, you will have the certain dependencies with the server such as datasources or JMS resources. Those resources must be there if you want to deploy. A best practice is of course to script these. You can use WLST to run .py script which create your resources. The only problem is you want to know for sure the resources are there?! If you want to be consistent in your roll-outs on Dev, Tst, Acc and Prd, you all want to do this in the same manner. Operations usually do the roll-out on Acc and Prd but how do make sure we do this in the exact same way in Dev and Tst?

If you are using a build pipeline using Jenkins (see here and here ) you can easily add a step in there which can create the resources for you by running a script. How do we do this?

We are going to make use of the weblogic-maven-plugin. See here for the documentation. First make sure you have installed the plugin into you local repository. As the documentation says, do the following:

  • Change directory to ORACLE_HOME\oracle_common\plugins\maven\com\oracle\maven\oracle-maven-sync\12.1.3
  • mvn install:install-file -DpomFile=oracle-maven-sync-12.1.3.pom -Dfile=oracle-maven-sync-12.1.3.jar
  • mvn com.oracle.maven:oracle-maven-sync:push -Doracle-maven-sync.oracleHome=c:\oracle\middleware\oracle_home\

You can check if it was successful by running

mvn help:describe -DgroupId=com.oracle.weblogic -DartifactId=weblogic-maven-plugin -Dversion=12.1.3-0-0

This should list all 24 goals of the plugin.

Now for the simple test, I have created a simple script test.py which adds a queue to the SOAJMSModule

try:
        print('--> about to connect to weblogic')
	connect('USERNAME','PASSWORD','t3://localhost:7001')
	print('--> about to create a queue ' + "MyQueue")
	edit()
	startEdit()
	cd('/JMSSystemResources/SOAJMSModule/JMSResource/SOAJMSModule')
	cmo.createQueue("MyQueue")
	cd('/JMSSystemResources/SOAJMSModule/JMSResource/SOAJMSModule/Queues/' + "MyQueue")
	set('JNDIName', 'jms.myqueueu')
	set('SubDeploymentName', 'SOASubDeployment')
	save()
	print('--> activating changes')
	activate()
	print('--> done')
except:
  print('Failed connecting to server')

I created a simple .pom file which only defines the build plugin:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

	<modelVersion>4.0.0</modelVersion>
	<groupId>nl.redrock</groupId>
	<artifactId>ResourceService</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>
	<description/>

	<build>
		<plugins>
			<plugin>
				<!-- This is the configuration for the weblogic-maven-plugin -->
				<groupId>com.oracle.weblogic</groupId>
				<artifactId>weblogic-maven-plugin</artifactId>
				<version>12.1.3-0-0</version>
				<configuration>
					<middlewareHome>/fmwhome/wls12130</middlewareHome>
				</configuration>
				<executions>
					<!-- Execute the appc goal during the package phase -->
					<execution>
						<id>wls-wlst-server</id>
						<phase>post-integration-test</phase>
						<goals>
							<goal>wlst-client</goal>
						</goals>
						<configuration>
							<executeScriptBeforeFile>true</executeScriptBeforeFile>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build> 
</project>

Now lets see if we can run the script by running the following:
mvn com.oracle.weblogic:weblogic-maven-plugin:wlst-client -DfileName=test.py

As you can see, the script was ran successfully:

Run

and to be sure we can check in the console:
console

And if you incorporate it into your Jenkins build pipeline, it could look something like this:

jenkins

So this is an easy way to consistently run scripts through-out your DTAP.

Some considerations though:

  • As you are running the script every time you are building a component, make sure the script takes in account that the resources might already have been created
  • The creation of some resources require a server restart. You can also restart servers using the weblogic plugin but I’m not yet sure what a good way of working is here

OFM 12C: Slow SB deployment issue

Working with 12C a while, we started noticing that the deployment of our SharedResources SB project started to slow down. This project contains re-usable resources and contracts. The strange thing was that it was only this specific SB project. All the other projects where fine. Both deploying in JDeveloper and deploying using Maven started at around 40 seconds but after a while, it went up to 10 minutes even which is of course not workable.

After some proper investigation and contact with Oracle Support we came to the conclusion that the Maven deployments where causing this. See bug 22051706: Maven OSB deploy causes open activation sessions on OSB in case of failure, which if unresolved cause slow deployment performance. So basically when your maven deployment fails, it causes an open session. If the session contains a lot of items, which our SharedResources project has, then even 1 or 2 open sessions will drastic slow your deployments down.

sessions

The solutions is either to use the servicebus console to navigate to the sessions tab at the bottom, select a session, take control of it and discard it. I think that also the $DOMAIN_HOME/osb folder holds the sessions which can be deleted on the server.

Another way is to remove them using a piece of code:

/* To run this, make sure the setDomainEnv.sh is sourced:
    cd $DOMAIN_HOME/bin
    . setDomainEnv.sh
*/

import weblogic.management.mbeanservers.domainruntime.DomainRuntimeServiceMBean;
import weblogic.management.jmx.MBeanServerInvocationHandler;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.JMXConnectorFactory;
import javax.management.ObjectName;
import com.bea.wli.sb.management.configuration.SessionManagementMBean;
import java.util.Set;
import java.util.Hashtable;
import java.util.HashSet;
import java.net.MalformedURLException;
import javax.naming.Context;
import java.io.IOException;

public class DiscardOSBSessions {
    public static final String hostname = "MY_HOSTNAME";
    public static final int port = 7001;
    public static final String username = "MY_USERNAME";
    public static final String password = "MY_PASSWORD";

    static public void main(String[] args)
    {
        JMXConnector conn = null;
        
        try
        {
            // get the jmx connector
            conn = initConnection(hostname, port, username, password);

            // get mbean connection
            MBeanServerConnection mbconn = conn.getMBeanServerConnection();

            // get the Session names:
            //com.bea:Name=ALSBConfiguration.weblogic,Location=AdminServer,Type=com.bea.wli.sb.management.configuration.ALSBConfigurationMBean
            ObjectName mbeanQuery = new ObjectName("*:Name=ALSBConfiguration.*,Type=com.bea.wli.sb.management.configuration.ALSBConfigurationMBean,Location=AdminServer");
            Set<ObjectName> mbeans = mbconn.queryNames(mbeanQuery, null);
            Set<String> sessionNames = new HashSet<String>();
            for (ObjectName mbeanName : mbeans) {
                sessionNames.add(mbeanName.getKeyPropertyList().get("Name").replace("ALSBConfiguration.",""));
            }
            
            System.out.println(sessionNames.size()+" open sessions:");
            if (!sessionNames.isEmpty()){
                for (String sessionName : sessionNames) {
                    System.out.println(" - "+sessionName);
                }

                System.out.println();
                System.out.println("Destroying the sessions:");
                
                // get domain service mbean. This is the topmost mbean
                DomainRuntimeServiceMBean domainService = (DomainRuntimeServiceMBean) MBeanServerInvocationHandler.newProxyInstance(mbconn, new ObjectName(DomainRuntimeServiceMBean.OBJECT_NAME));

                // obtain session management mbean to destroy a session.
                // This mbean instance can be used more than once to create/discard/commit many sessions
                SessionManagementMBean sm = (SessionManagementMBean) domainService.findService(SessionManagementMBean.NAME, SessionManagementMBean.TYPE, null);

                for (String sessionName : sessionNames) {
                    System.out.println(" - "+sessionName);
                    sm.discardSession(sessionName);
                }
            }
            System.out.println();
            System.out.println("Successful completion");
        }
        catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null)
                try {
                    conn.close();
                } catch (Exception e) {                
                    e.printStackTrace();
                }
        }
    }

    private static JMXConnector initConnection(String hostname, int port, String username, String password) throws IOException,MalformedURLException
    {
        JMXServiceURL serviceURL = new JMXServiceURL("t3", hostname, port, "/jndi/" + DomainRuntimeServiceMBean.MBEANSERVER_JNDI_NAME);
        Hashtable<String, String> h = new Hashtable<String, String>();
        h.put(Context.SECURITY_PRINCIPAL, username);
        h.put(Context.SECURITY_CREDENTIALS, password);
        h.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote");
        return JMXConnectorFactory.connect(serviceURL, h);
    }
}

As you can see, you can only run this on the appropriate server with the right classes on the classpath. I tried to Mavenize it into a maven project with the proper dependencies but I wasn’t able to find the right jar files for:

  • weblogic.management.mbeanservers.domainruntime.DomainRuntimeServiceMBean
  • weblogic.management.jmx.MBeanServerInvocationHandler
  • com.bea.wli.sb.management.configuration.SessionManagementMBean

If anyone can help me out here….that would be great!

A feature request has been made to add the ‘discard session’ parameter to the Maven properties so hopefully this will come in future releases.

Automated acceptance testing using Fitnesse, Java and the OSB

When I was at Xebicon this year I saw a talk from Dave Farley. At 26 minutes or so he starts talking about Domain Specific Languages. Making an abstraction layer on top of your system where your tests run against so when the underlying system changes, you only have to adjust your layer in between instead of all the tests. This idea got me thinking of how we could be able to do this as I am mainly working in the integration business building services. This lead me to Fitnesse. Fitnesse has been around for many years now but I never had the chance to do something with it. But what is Fitnesse?

fitnesse

Fitnesse is a lightweight, open-source framework that makes it easy for software teams to:

  • Collaboratively define Acceptance Tests, web pages containing simple tables of inputs and expected outputs.
  • Run those tests and see the results.

I was interested if I could make a simple abstraction from a service which I build using the OSB and got Fitnesse to run tests against it. Also I was interested if you could then possibly wire it into a build/release pipeline.

The first thing was to get a simple webservice running. I used an old 12C webservice of mine which I build in a previous post. It is a conversion rate webservices with a SOAP or REST entry. I will be using the SOAP one for now. The service takes two parameters, a from and a to currency and the fetches the conversionrate. I deployed it and tested it and as you can see, it works.

soap-service

Next is to get Fitnesse up and running. This is as simple as downloading it and running it using Java like java -jar fitnesse-standalone.jar. When it is up, you can go to http://localhost:80 to see the Fitnesse wiki startpage.

fitnesse-wiki

The next thing is to create a test page. Click the Add link and choose Test Page. Give the page a name and a description. If you start a page name with Test….Fitnesse will automatically recognize it as a test page. Save it for now because we first have to make a piece of Java which will be the abstraction layer between the tests and our OSB webservice. Open your favourite IDE and create a simple Java project based on Maven. I am going to use jax-ws to create a webservice client and write a fixture. A fixture is a Java class which can be extended so Fitnesse can inject the test variables and retrieve the results making use of reflection and such. So we need to add a Fitnesse dependency in our pom and I am going to use the jax-ws maven plugin to generate classes based on the service WSDL. You can do this in various ways but for now I am going to use plain Java. My pom looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 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>nl.redrock</groupId>
    <artifactId>FitnesseTest</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.fitnesse</groupId>
            <artifactId>fitnesse</artifactId>
            <version>20081201</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxws-maven-plugin</artifactId>
            <version>1.12</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- usage of jax-ws maven plugin-->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>1.12</version>
                <executions>
                    <execution>
                        <id>wsimport-from-jdk</id>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--wsdls file directory -->
                    <wsdlDirectory>src/main/resources/wsdls</wsdlDirectory>
                    <!-- which wsdl file -->
                    <wsdlFiles>
                        <wsdlFile>conversionrateservice.wsdl</wsdlFile>
                    </wsdlFiles>
                    <!-- Keep generated files -->
                    <keep>true</keep>
                    <!-- Package name -->
                    <packageName>nl.redrock.fitnessetest</packageName>
                    <!-- generated source files destination-->
                    <sourceDestDir>target/generated-code/src</sourceDestDir>
                </configuration>
            </plugin>
            <!-- adding generated source -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>target/generated-code/src</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

As you can see I refer to src/main/resources/wsdls where my service WSDL is located. I can now just run mvn generate-sources to generate the classes I need for calling the OSB service. When I ran mvn generate-sources I have the following classes:

generated-sources

I can now write the fixture and use JUnit to test it. I am going to use a simple fixture but as your can see here there are many fixtures which you can extend. Mine looks like this:

package nl.redrock.fitnessetest.fixtures;

import fit.*;
import nl.redrock.fitnessetest.ConversionRateService_Service;
import nl.redrock.fitnessetest.Currency;
import nl.redrock.fitnessetest.GetConversionRateRequest;
import nl.redrock.fitnessetest.GetConversionRateResponse;

/**
 *
 * @author Hugo
 */
public class ConversionRateServiceFixture extends ColumnFixture {
    
    public String from;
    public String to;
    
    public double getConverionRate() throws Exception
    {
        ConversionRateService_Service client = new ConversionRateService_Service();
        GetConversionRateRequest req = new GetConversionRateRequest();
        req.setFromCurrency(Currency.fromValue(this.from));
        req.setToCurrency(Currency.fromValue(this.to));
        
        GetConversionRateResponse response = client.getConversionRateServicePort().getConversionRate(req);
        return response.getConversionRateResult();
    }
    
}

As you can see it extends the class ColumnFixture and I have 2 public variables to and from.

The next thing is to write a JUnit test to see if the fixture works:

package nl.redrock.fitnessetest;

import nl.redrock.fitnessetest.fixtures.ConversionRateServiceFixture;
import org.junit.Test;

public class ConversionRateServiceTest {

    @Test
    public void ConversionRateClientTest() throws Exception {
        
        ConversionRateServiceFixture fix = new ConversionRateServiceFixture();
        fix.from="EUR";
        fix.to="USD";
        System.out.println(fix.getConverionRate());
    }
}

I have no assertion here, just a System.out to see if the service works. Lets run it and see the magic.

junit-test

Perfect. The last thing we have to do now is to package everything into a jar so we can tell Fitnesse to put it on the classpath. Just run mvn clean package and it should create your jar in the target directory. Now lets get back to Fitnesse. Open the page you have just create and edit it.

Add the following code to the wiki page:

!path D:\Workspace\Java\FitnesseTest\target\FitnesseTest-1.0.jar

!|nl.redrock.fitnessetest.fixtures.ConversionRateServiceFixture |
| to | from | getConverionRate? |
| USD | EUR | 1.111 |
| EUR | EUR | 1.0 |

The !path will tell Fitnesse that it should put this jar on its classpath when running the tests. The
!|nl.redrock.fitnessetest.fixtures.ConversionRateServiceFixture | will tell which is the fixture class. After that it is just easy. The first two columns say which parameters can be used as input, and the third on for output. Notice the questionmark at the end of getConverionRate?.
The last 2 rows are just testcases.

Now save the page and press the Test button at the top of the screen.

fitnesse-test

There we go. We ran the 2 testcases and got the results back. I checked Jenkins for plugins and there is a plugin which enables you to run the Fitnesse tests so you easily add them to release pipeline.

So

  • The abstraction layer between your service and the tests is quite a big benefit. Lets say the contract of the service has a breaking change. We add a mandatory field. Normally you would have to change all your existing testcases. With the abstraction layer you would be able to just fix this in the fixture (if your existing testcases are unaffected by the added field of course)
  • another nice feature is that anyone can make testcases without any knowledge of soap/xml/webservice. If your fixture is nicely programmed, even business people could write tests. Quite often acceptence testers are not used to testing services without a GUI. With this its possible to do that.
  • last one is that you completely automate it. Quite often acceptance tests are done by hand which take quite some time and effort. Now you automate them and reduce the time you can possibly deliver your software to production.

Some considerations though are:

  • You do need to put in the effort to make the abstraction layer. In the beginning this will take more effort but you will earn this back quite fast when you need to regression tests or so. In the long run….this will pay of!
  • Fitnesse can be used by different languages such as .Net and Java but you will still need people who are capable of this.

SOA Suite 12C: Simple example of using the Spring component

A few days ago, we needed a some business functionality to validate an IBAN account number. You can use inline Java in your BPEL process but there are more elegant ways as you don’t want to pollute your orchestration layer with business functionality. An nice and elegant way, in my opinion, is the use of the Spring component. By using this component you can expose a Java bean as simple as you would by calling another webservice. This way the business functionality can be bundled in a nice .jar which you can also nicely test separate from the rest. Let’s see how this all works.

First of all, I will need to create a simple jar file which contains the function I want to use with an appriopriate interface. I my case I will create a BankAccountUtils class and IBankAccountUtils interface and a simple JUnit testcase to see if my function does what it has to do.

package nl.redrock.soa.utils;

/**
 * BankAccountUtils interface
 * @author Hugo
 */
public interface IBankAccountUtils {
    
    /**
     * Check an account number
     * @param account the account number to check
     * @return true of a valid IBAN, false if not
     */
    public boolean ibanTest(String account);
}
package nl.redrock.soa.utils;

import java.math.BigInteger;

/**
 * BankAccountUtils class
 * @author Hugo
 */
public class BankAccountUtils implements IBankAccountUtils {

    public final int IBANNUMBER_MIN_SIZE = 15;
    public final int IBANNUMBER_MAX_SIZE = 34;
    public final BigInteger IBANNUMBER_MAGIC_NUMBER = new BigInteger("97");

    @Override
    public boolean ibanTest(String accountNumber) {

        boolean result;

        String newAccountNumber = accountNumber.trim();

        // Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid. We could also check
        // for specific length according to country, but for now we won't
        if (newAccountNumber.length() < IBANNUMBER_MIN_SIZE || newAccountNumber.length() > IBANNUMBER_MAX_SIZE) {
            result = false;
        } else {

            // Move the four initial characters to the end of the string.
            newAccountNumber = newAccountNumber.substring(4) + newAccountNumber.substring(0, 4);

            // Replace each letter in the string with two digits, thereby expanding the string, where A = 10, B = 11, ..., Z = 35.
            StringBuilder numericAccountNumber = new StringBuilder();
            for (int i = 0; i < newAccountNumber.length(); i++) {
                numericAccountNumber.append(Character.getNumericValue(newAccountNumber.charAt(i)));
            }

            // Interpret the string as a decimal integer and compute the remainder of that number on division by 97.
            BigInteger ibanNumber = new BigInteger(numericAccountNumber.toString());
            result = ibanNumber.mod(IBANNUMBER_MAGIC_NUMBER).intValue() == 1;
        }
        return result;
    }
}
import nl.redrock.soa.utils.BankAccountUtils;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * Simple JUnit test class for the BankAccountUtils
 * @author Hugo
 */
public class BankAccountUtilsTest {
    
   
    @Test
    public void ibanTest(){
        assertTrue(new BankAccountUtils().ibanTest("NL28RBOS0569988888"));
        assertTrue(new BankAccountUtils().ibanTest("PL61109010140000071219812874"));
        assertTrue(new BankAccountUtils().ibanTest("CH3608387000001080173"));
        assertTrue(new BankAccountUtils().ibanTest("IT60X0542811101000000123456"));
        assertTrue(new BankAccountUtils().ibanTest("CY17002001280000001200527600"));
        assertTrue(new BankAccountUtils().ibanTest("FI2112345600000785"));
    }
}

Lets run the tests and see if the functionality works:
junit

Now package the class and interface in a nice .jar and we are done for this bit!

Now lets fire up JDeveloper and create a simple composite. Right click on the Project and choose Project Properties. Go to the Libraries and Classpath tab and add the .jar of the Java project we have created above. You can also just drop the .jar in the SCA-INF/lib folder of your project.

ProjectProperties

I created a BPEL process which should process a money transfer.

project

The in- and output look like this:

<?xml version="1.0" encoding="UTF-8"?> 
<schema attributeFormDefault="unqualified"
	elementFormDefault="qualified"
	targetNamespace="http://xmlns.oracle.com/SoaApplication/SpringComponent/ProcesMoneyTransfer"
	xmlns="http://www.w3.org/2001/XMLSchema">
	<element name="process">
		<complexType>
			<sequence>
				<element name="fromAccount" type="string"/>
                                <element name="toAccount" type="string"/>
                                <element name="amount" type="integer"/>
			</sequence>
		</complexType>
	</element>
	<element name="processResponse">
		<complexType>
			<sequence>
				<element name="result" type="boolean"/>
			</sequence>
		</complexType>
	</element>
</schema>

Now for the interesting part. We want to check if the fromAccount and the toAccount are valid IBAN accounts in the BPEL. First we drag the Spring component onto the composite and name the Spring Context.

SpringContext

A new Spring context file will be created. For all you Java-Spring people….this file will look very familiar in the next couple of steps. Open the Spring xml file and select the Weblogic SCA components.

SpringContext

Now drag the Service component onto the XML file. Give it a name and a target and select the interface from the .jar using the cog. For some documentation see here

Service

Save and close it. When you return to your composite you can see that the Spring component has an interface now.

Interface

We do need to make some extra adjustments. We also need to declare the bean which actually holds the implementation so go back and open the context and add the bean and we also need to add an extra interface.java element to the sca:service element to tell it is a Java interface.

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="BankAccountUtils" target="int" type="nl.redrock.soa.utils.IBankAccountUtils">
    <interface.java interface="nl.redrock.soa.utils.IBankAccountUtils"/>
  </sca:service>
  <bean name="int" class="nl.redrock.soa.utils.BankAccountUtils"/>
</beans>

Also one thing you will have to do is that you will need to replace the whole spring schema-location with a much simpler version as the one generated or else you will get an error during deployment. Found the solution for this here.

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">

Now we can wire the BPEL to the Spring component.

CreateInterface

You can see it has created a WSDL and a wrapper WSDL in the WSDLs folder. Now you can just call the bean like any other normal partner link.

BPEL

Don’t forget to drop the .jar file also on your server in the /lib directory. The server will need a restart after this to pick up the new .jar file.

Now build and deploy it and call it from the EM test console:

ResultTrue

and the result:

ResultTrue

And there we go. A nice and simple way to implement business functionality which is re-usable and doesn’t clutter your orchestration layer.

Oracle Fusion Middleware 12C: Deploy the MDS using Maven

In two previous posts here and here, I demonstrated how you can setup CI for OFM 12C projects. One of the things I hadn’t figured out yet was how I could deploy the MDS using Maven. After a vague hint on the internet, Oracle’s response that this feature wasn’t officially supported yet and some trial and error I came across the solution. It is actually quite simple.

Basically it comes down to this:

  • Create a zip file of the files you want to stick into the MDS
  • Use the com.oracle.soa.plugin:oracle-soa-plugin:deploy maven goal to deploy it

That wasn’t that hard as you can see :) Let’s go into a bit more detail. You can use a command line to create the zip or use the maven-assembly-plugin to give you a bit more flexibility. I used the last option because I wanted to filter certain files. Within my Jenkins job, I checked out my MDS files and zipped them using the package goal into a file called sca-MDS_rev1.0.jar. Look in the plugin section in the pom file down below to see about the configuration of the assembly plugin. The pom file is just a copy of a normal soa composite project.

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <!--
           This POM was automatically generated during SOA project creation.           
           This POM relates to this SOA Composite, i.e. the one in this same directory.
           There is another POM in the SOA Application directory which handles
           the whole SOA Application, which may contain additional projects. 
        -->
    <modelVersion>4.0.0</modelVersion>
    <groupId>nl.cvgz</groupId>
    <artifactId>MDS</artifactId>
    <version>1.0</version>
    <packaging>sar</packaging>
    
    <!--
           The parent points to the common SOA parent POM.  That is a special POM that is
           shipped by Oracle as a point of customization (only). You can add default values
           for properties like serverUrl, etc. to the SOA common parent POM, so that you
           do not have to specify them over and over in every project POM.
    --> 
    <parent>
        <groupId>com.oracle.soa</groupId>
        <artifactId>sar-common</artifactId>
        <version>12.1.3-0-0</version>
    </parent>
    
    <properties>
        <!-- These parameters are used by the compile goal -->
        <scac.input.dir>${project.basedir}\SOA/</scac.input.dir>
        <scac.output.dir>${project.basedir}/target</scac.output.dir>
        <scac.input>${scac.input.dir}/composite.xml</scac.input>
        <scac.output>${scac.output.dir}/out.xml</scac.output>
        <scac.error>${scac.output.dir}/error.txt</scac.error>
        <scac.displayLevel>1</scac.displayLevel>
        <!-- if you are using a config plan, uncomment the following line and update to point
             to your config plan -->
        <!--<configplan>${scac.input.dir}/configplan.xml</configplan>-->
        
        <!-- These parameters are used by the deploy and undeploy goals --> 
        <composite.name>${project.artifactId}</composite.name>
        <composite.revision>${project.version}</composite.revision>
        <composite.partition>default</composite.partition>        
        <serverUrl>${oracleServerUrl}</serverUrl>        
        <user>${oracleUsername}</user>
        <password>${oraclePassword}</password>
        <overwrite>true</overwrite>
        <forceDefault>true</forceDefault>
        <regenerateRulebase>false</regenerateRulebase>
        <keepInstancesOnRedeploy>false</keepInstancesOnRedeploy>
        
        <!-- These parameters are used by the test goal 
         if you are using the sca-test (test) goal, you need to uncomment the following
             line and point it to your jndi.properties file. --> 
             
        <!--<jndi.properties.input>UNDEFINED</jndi.properties.input>-->
        <scatest.result>${scac.output.dir}/testResult</scatest.result>
        <!--  input is the name of the composite to run test suties against -->
        <input>${project.artifactId}</input>
        
        <!--<scac.ant.buildfile>${env.MW_HOME}/soa/bin/ant-sca-compile.xml</scac.ant.buildfile>
        <sca.ant.testfile>${env.MW_HOME}/soa/bin/ant-sca-test.xml</sca.ant.testfile>
        -->
        
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>com.oracle.soa.plugin</groupId>
                <artifactId>oracle-soa-plugin</artifactId>
                <version>12.1.3-0-0</version>
                <configuration>
                    <compositeName>${project.artifactId}</compositeName>
                    <composite>${scac.input}</composite>
                    <sarLocation>${scac.output.dir}/sca-${project.artifactId}_rev${composite.revision}.jar</sarLocation>
                    <serverUrl>${serverUrl}</serverUrl>
                    <user>${user}</user>
                    <password>${password}</password>
                    <!-- Note: compositeRevision is needed to package, revision is needed to undeploy -->
                    <compositeRevision>${composite.revision}</compositeRevision>
                    <revision>${composite.revision}</revision>
                    <scacInputDir>${scac.input.dir}</scacInputDir>                    
                    <input>${input}</input> 
                </configuration>
                <!-- extensions=true is needed to use the custom sar packaging type -->
                <extensions>true</extensions>
            </plugin>
	    <!-- plugin om te zorgen dat de apps directory gezipt wordt voor de MDS -->
	    <plugin>
			<artifactId>maven-assembly-plugin</artifactId>
			<version>2.4.1</version>
			<configuration>
				<finalName>sca</finalName>
				<descriptors>
					<descriptor>zip-assembly-descriptor.xml</descriptor>
				</descriptors>
			</configuration>
			<executions>
				<execution>
					<id>make-assembly</id>
					<phase>package</phase>
					<goals>
						<goal>single</goal>
					</goals>
				</execution>
		     </executions>
		</plugin>
        </plugins>
    </build>
</project>

Here is the zip-assembly-descriptor.xml

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
  <id>MDS_rev1.0</id>
  <baseDirectory>/</baseDirectory>
  <formats>
    <format>jar</format>
  </formats>
  <fileSets>
	<fileSet>
      <directory>../../../GedeeldeObjecten/apps</directory>
      <outputDirectory>/</outputDirectory>
	  <includes>
        <include>services/**</include>
		<include>dvm/**</include>
      </includes>
    </fileSet>
	
  </fileSets>
</assembly>

As you can see I create a jar which includes my services and dvm directory. One thing to take in account is that you don’t need an apps directory in the zip. At first I created an apps directory as a top directory but when I deployed this, my MDS had a apps/apps structure so I removed this. My sca-MDS_rev1.0.jar has the following structure:

-dvm
   -MyDVM.dvm
-services
   -HelloService_1.0
       -HelloService_1.0.xsd
       -HelloService_1.0.wsdl

The only now left to do is to point the ${scac.output.dir}/sca-${project.artifactId}_rev${composite.revision}.jar in the pom to the right location where your jar file is located.

When you run the com.oracle.soa.plugin:oracle-soa-plugin:deploy goal, maven will deploy you MDS jar like this:

[INFO] --- maven-assembly-plugin:2.4.1:single (make-assembly) @ MDS ---
[INFO] Reading assembly descriptor: zip-assembly-descriptor.xml
[INFO] Building jar: D:\tomcatfiles\.jenkins\jobs\SOA - MDS\workspace\MDS\soa\MDS\target\sca-MDS_rev1.0.jar
[INFO] 
[INFO] --- oracle-soa-plugin:12.1.3-0-0:deploy (default-cli) @ MDS ---
[INFO] ------------------------------------------------------------------------
[INFO] ORACLE SOA MAVEN PLUGIN - DEPLOY COMPOSITE
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] setting user/password..., user=weblogic
Processing sar=D:\tomcatfiles\.jenkins\jobs\SOA - MDS\workspace\MDS\soa\MDS/target/sca-MDS_rev1.0.jar
Adding shared data file - D:\tomcatfiles\.jenkins\jobs\SOA - MDS\workspace\MDS\soa\MDS\target\sca-MDS_rev1.0.jar
INFO: Creating HTTP connection to host:MY_SERVER, port:7010
INFO: Received HTTP response from the server, response code=200
---->Deploying composite success.
[JENKINS] Archiving disabled
[JENKINS] Archiving disabled[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.313 s

[INFO] Finished at: 2015-05-23T02:59:22+02:00
[INFO] Final Memory: 36M/360M
[INFO] ------------------------------------------------------------------------
[workspace] $ cmd /c call D:\ProgramFiles\Apache\Tomcat8.0\temp\hudson4288153212971789360.bat
channel stopped

CI using Oracle Fusion Middleware 12C: Part 2. Building a SB and SOA project using maven and the MDS

In the part 1 I have shown how to setup a simple CI environment and how to build a Service Bus project using Maven. In this part I will try to make a release pipeline which builds, deploys, tests, packages and release a whole service using Jenkins and if all successful and finally install the artifact in Nexus.

Lets start where we left of. Startup Tomcat and log into Jenkins. We need some sort of plugin to be able to run multiple actions in a sequence. Jenkins has alot of plugins but the one which I am going to use is the MultiJob one. Go to Manage Jenkins->Manage plugins, choose the available tab, check the Multijob plugin and click Install without restart.

Multijob

The multi-job plugin can chain jobs together and share variables and artifact between jobs. You can make very intricate jobs but for now I will keep it simple. I will make 1 job that will:

  1. Build the service bus component which also refers to a SharedObjects project and deploy it to my server
  2. Build the soa component which also refers to the MDS and deploy it to my server
  3. Run the matching soap ui test
  4. If succesfull, install the artifact to nexus

I have created a simple HelloService which first goes to the SB and then routes to a SOA component. The SB component makes use of a SharedObjects SB project which holds the WSDL and XSD. This project is setup so you don’t have to sync between this project and the MDS. This because the SB isn’t able yet to access the MDS. The SOA component does nothing else the return a string response. So the setup will look like this:

HelloService

So the first job we have to create is one which builds and deploys an SB project using maven. We will have to have 2 jobs as we want to deploy the SharedObjects project and we want to deploy the SB service afterwards. Lets make the SharedObject first. This is an easy one as it has no references to any other project.

Create a new Jenkins job called OSB – SharedObjects which is based on a maven project. The .pom file is very simple. This will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>com.oracle.servicebus</groupId>
		<artifactId>sbar-project-common</artifactId>
		<version>12.1.3-0-0</version>
	</parent>

	<groupId>nl.cvgz</groupId>
	<artifactId>GedeeldeObjecten</artifactId>
	<version>1.0</version>
	<packaging>sbar</packaging>
	<description/>
</project>

As a maven goal we can just use the following command to build and deploy the SharedObject project to the designated server:

clean pre-integration-test -DoracleHome=${oracleHome} -DoracleServerUrl=${osbServername} -DoracleUsername=${osbUsername} -DoraclePassword=${osbPassword}

You can insert the variables from a property file as you can see. Now lets try this first job and see how it runs.

SharedObjects-jenkins

Started by user Hugo Hendriks
[EnvInject] - Loading node environment variables.
Building in workspace D:\tomcatfiles\.jenkins\jobs\OSB - GedeeldeObjecten\workspace
Cleaning local Directory GedeeldeObjecten
Checking out svn://hendriksh@u10023o/mmi/trunk/OSB/GedeeldeObjecten at revision '2015-02-18T19:18:29.973 +0100'
A         GedeeldeObjecten.jpr
A         apps
A         apps\services
A         apps\services\HelloService
A         apps\services\HelloService\1.0
A         apps\services\HelloService\1.0\HelloService.wsdl
A         apps\services\HelloService\1.0\cdm.xsd
A         apps\services\HelloService\1.0\HelloService.xsd
A         servicebus.sboverview
A         alerts
A         alerts\Log.alert
A         alerts\ErrorDestination.alert
A         pom.xml
 U        .
At revision 927
Cleaning local Directory Build
Checking out svn://hendriksh@u10023o/mmi/trunk/build at revision '2015-02-18T19:18:29.973 +0100'
A         build-env-TST.properties
A         build-env-ONT.properties
At revision 927
no change for svn://hendriksh@u10023o/mmi/trunk/OSB/GedeeldeObjecten since the previous build
no change for svn://hendriksh@u10023o/mmi/trunk/build since the previous build
[EnvInject] - Executing scripts and injecting environment variables after the SCM step.
[EnvInject] - Injecting as environment variables the properties file path 'Build/build-env-ONT.properties'
[EnvInject] - Variables injected successfully.
Parsing POMs
[GedeeldeObjecten] $ D:\ProgramFiles\Java\jdk1.7.0_71/bin/java -cp D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-agent-1.5.jar;D:\ProgramFiles\Apache\apache-maven-3.2.3\boot\plexus-classworlds-2.5.1.jar;D:\ProgramFiles\Apache\apache-maven-3.2.3/conf/logging jenkins.maven3.agent.Maven31Main D:\ProgramFiles\Apache\apache-maven-3.2.3 D:\ProgramFiles\Apache\Tomcat8.0\webapps\jenkins\WEB-INF\lib\remoting-2.48.jar D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-interceptor-1.5.jar D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven3-interceptor-commons-1.5.jar 58198
<===[JENKINS REMOTING CAPACITY]===>���channel started
Executing Maven:  -B -f D:\tomcatfiles\.jenkins\jobs\OSB - GedeeldeObjecten\workspace\GedeeldeObjecten\pom.xml clean pre-integration-test -DoracleHome=[MY-ORACLEHOME] -DoracleServerUrl=http://[MY-SERVER] -DoracleUsername=[MY-USER] -DoraclePassword=[MY-PASSWORD]
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building GedeeldeObjecten 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ GedeeldeObjecten ---
[INFO] 
[INFO] --- oracle-servicebus-plugin:12.1.3-0-0:package (default-package) @ GedeeldeObjecten ---
[INFO] 
[INFO] --- oracle-servicebus-plugin:12.1.3-0-0:deploy (default-deploy) @ GedeeldeObjecten ---
[INFO] Service Bus Archive deployed using session Service_Bus_Maven-GedeeldeObjecten-1424283534524.
Diagnostic XML Bean debug log file created: C:\Windows\TEMP\xmlbeandebug7850265000274769465.log
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:37 min
[INFO] Finished at: 2015-02-18T19:21:15+01:00
[INFO] Final Memory: 22M/362M
[INFO] ------------------------------------------------------------------------
[JENKINS] Archiving D:\tomcatfiles\.jenkins\jobs\OSB - GedeeldeObjecten\workspace\GedeeldeObjecten\pom.xml to nl.cvgz/GedeeldeObjecten/1.0/GedeeldeObjecten-1.0.pom
[JENKINS] Archiving D:\tomcatfiles\.jenkins\jobs\OSB - GedeeldeObjecten\workspace\GedeeldeObjecten\.data\maven\sbconfig.sbar to nl.cvgz/GedeeldeObjecten/1.0/GedeeldeObjecten-1.0.sbar
D:/tomcatfiles/.jenkins/jobs/OSB - GedeeldeObjecten/workspace/GedeeldeObjecten/pom.xml is not inside D:/tomcatfiles/.jenkins/jobs/OSB - GedeeldeObjecten/workspace/GedeeldeObjecten/GedeeldeObjecten/; will archive in a separate pass
D:/tomcatfiles/.jenkins/jobs/OSB - GedeeldeObjecten/workspace/GedeeldeObjecten/.data/maven/sbconfig.sbar is not inside D:/tomcatfiles/.jenkins/jobs/OSB - GedeeldeObjecten/workspace/GedeeldeObjecten/GedeeldeObjecten/; will archive in a separate pass

channel stopped
Finished: SUCCESS

As you can see….success!

The next bit will be to make a job which deploys the HelloService SB component. This job will actually be quite the same as the previous one except for the fact that it references the SharedObjects project. If you run the normal maven build, it will complain that it can’t find the shared resources. The way to solve this, is to also checkout the SharedObject project during the build in your workspace. This way it can find the project compile time.

Started by user Hugo Hendriks
[EnvInject] - Loading node environment variables.
Building in workspace D:\tomcatfiles\.jenkins\jobs\OSB - Service\workspace
Cleaning local Directory HelloService_v1.0
Checking out svn://hendriksh@u10023o/mmi/trunk/OSB/HelloService_v1.0 at revision '2015-02-18T19:31:12.398 +0100'
A         pom.xml
A         Business
A         Business\HelloService.bix
A         LogPipeline.pipeline
A         HelloPipeline.pipeline
A         Proxy
A         HelloService_v1.0.jpr
A         ValidatePipeline.pipeline
A         HelloService.proxy
A         servicebus.sboverview
At revision 927
Cleaning local Directory GedeeldeObjecten
Checking out svn://hendriksh@u10023o/mmi/trunk/OSB/GedeeldeObjecten at revision '2015-02-18T19:31:12.398 +0100'
A         GedeeldeObjecten.jpr
A         apps
A         apps\services
A         apps\services\HelloService
A         apps\services\HelloService\1.0
A         apps\services\HelloService\1.0\HelloService.wsdl
A         apps\services\HelloService\1.0\cdm.xsd
A         apps\services\HelloService\1.0\HelloService.xsd
A         servicebus.sboverview
A         alerts
A         alerts\Log.alert
A         alerts\ErrorDestination.alert
A         pom.xml
 U        .
At revision 927
Cleaning local Directory Build
Checking out svn://hendriksh@u10023o/mmi/trunk/build at revision '2015-02-18T19:31:12.398 +0100'
A         build-env-TST.properties
A         build-env-ONT.properties
At revision 927
Cleaning local Directory Customization
Checking out svn://hendriksh@u10023o/mmi/trunk/OSB/Customization at revision '2015-02-18T19:31:12.398 +0100'
A         all_BS_ACC.xml
A         all_BS_TST.xml
A         all_BS_ONT.xml
At revision 927
no revision recorded for svn://hendriksh@u10023o/mmi/trunk/OSB/HelloService_v1.0 in the previous build
no change for svn://hendriksh@u10023o/mmi/trunk/build since the previous build
no change for svn://hendriksh@u10023o/mmi/trunk/OSB/Customization since the previous build
[EnvInject] - Injecting environment variables from a build step.
[EnvInject] - Injecting as environment variables the properties file path 'Build/build-env-ONT.properties'
[EnvInject] - Variables injected successfully.
Parsing POMs
Modules changed, recalculating dependency graph
[HelloService_v1.0] $ D:\ProgramFiles\Java\jdk1.7.0_71/bin/java -cp D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-agent-1.5.jar;D:\ProgramFiles\Apache\apache-maven-3.2.3\boot\plexus-classworlds-2.5.1.jar;D:\ProgramFiles\Apache\apache-maven-3.2.3/conf/logging jenkins.maven3.agent.Maven31Main D:\ProgramFiles\Apache\apache-maven-3.2.3 D:\ProgramFiles\Apache\Tomcat8.0\webapps\jenkins\WEB-INF\lib\remoting-2.48.jar D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-interceptor-1.5.jar D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven3-interceptor-commons-1.5.jar 58247
<===[JENKINS REMOTING CAPACITY]===>���channel started
Executing Maven:  -B -f D:\tomcatfiles\.jenkins\jobs\OSB - Service\workspace\HelloService_v1.0\pom.xml clean pre-integration-test -DoracleHome=[MY-ORACLEHOME] -DoracleServerUrl={MY-SERVER} -DoracleUsername=[MY-USER] -DoraclePassword=[MY-PASSWORD]
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building HelloService 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ HelloService ---
[INFO] 
[INFO] --- oracle-servicebus-plugin:12.1.3-0-0:package (default-package) @ HelloService ---
[INFO] 
[INFO] --- oracle-servicebus-plugin:12.1.3-0-0:deploy (default-deploy) @ HelloService ---
[INFO] Service Bus Archive deployed using session Service_Bus_Maven-HelloService-1424284297240.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 34.955 s
[INFO] Finished at: 2015-02-18T19:31:56+01:00
[INFO] Final Memory: 23M/226M
[INFO] ------------------------------------------------------------------------
Waiting for Jenkins to finish collecting data
[JENKINS] Archiving D:\tomcatfiles\.jenkins\jobs\OSB - Service\workspace\HelloService_v1.0\pom.xml to nl.cvgz/HelloService/1.0/HelloService-1.0.pom
[JENKINS] Archiving D:\tomcatfiles\.jenkins\jobs\OSB - Service\workspace\HelloService_v1.0\.data\maven\sbconfig.sbar to nl.cvgz/HelloService/1.0/HelloService-1.0.sbar
channel stopped
Finished: SUCCESS

Also success! So we now have 2 separate jobs:

      OSB – SharedObjects = which builds and deploys your SharedObject project.
      OSB – Service = which builds and deploys your HelloService project.

The next thing is to do the same for the SOA component. I actually want a SOA – MDS jenkins job which zips and deploys the MDS but it seems Oracle doesn’t support the deployment of the MDS through maven yet. I have heard rumors though that you can zip it and deploy it using the normal maven plugins. I am able to zip the apps directory but I haven’t been able to deploy it yet so if anyone knows how to do this….let me know :)

The last job is a SOA – Service job which builds and deploys the SOA component. When you create a SOA project you get a pom with it. To make this work with the MDS you do have to tweak it here and there. The problem is that the MDS reference is made through the application which references the adf-config.xml. Lets start of with the adf-config.xml which is located in .adf\META-INF. You should make a MDS like this:

<adf-mds-config xmlns="http://xmlns.oracle.com/adf/mds/config">
    <mds-config xmlns="http://xmlns.oracle.com/mds/config">
      <persistence-config>
        <metadata-namespaces>
          <namespace path="/apps" metadata-store-usage="mstore-usage_apps"/>
        </metadata-namespaces>
        <metadata-store-usages>
          <metadata-store-usage id="mstore-usage_apps">
            <metadata-store class-name="oracle.mds.persistence.stores.file.FileMetadataStore">
              <property name="metadata-path" value="${soamds.apps.home}" />
            </metadata-store>
          </metadata-store-usage>
        </metadata-store-usages>
      </persistence-config>
    </mds-config>
  </adf-mds-config>

As you can see we made the metadata-path for the /apps settable using a variable soamds.apps.home. So now you can set the directory of your apps of your application which holds the soa project. Now for the last bit….the .pom file of you soa project. As I said, you will have to tweak some settings here as it doesn’t completely work out of the box. You should make the following adjustments:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <!--
           This POM was automatically generated during SOA project creation.           
           This POM relates to this SOA Composite, i.e. the one in this same directory.
           There is another POM in the SOA Application directory which handles
           the whole SOA Application, which may contain additional projects. 
        -->
    <modelVersion>4.0.0</modelVersion>
    <groupId>VGZ-SOA-Application</groupId>
    <artifactId>HelloService</artifactId>
    <version>1.0</version>
    <packaging>sar</packaging>
    
    <!--
           The parent points to the common SOA parent POM.  That is a special POM that is
           shipped by Oracle as a point of customization (only). You can add default values
           for properties like serverUrl, etc. to the SOA common parent POM, so that you
           do not have to specify them over and over in every project POM.
    --> 
    <parent>
        <groupId>com.oracle.soa</groupId>
        <artifactId>sar-common</artifactId>
        <version>12.1.3-0-0</version>
    </parent>
    
    <properties>
        <!-- These parameters are used by the compile goal -->
        <scac.input.dir>${project.basedir}\SOA/</scac.input.dir>
        <scac.output.dir>${project.basedir}/target</scac.output.dir>
        <scac.input>${scac.input.dir}/composite.xml</scac.input>
        <scac.output>${scac.output.dir}/out.xml</scac.output>
        <scac.error>${scac.output.dir}/error.txt</scac.error>
        <scac.displayLevel>1</scac.displayLevel>
        <!-- These parameters are used by the deploy and undeploy goals --> 
        <composite.name>${project.artifactId}</composite.name>
        <composite.revision>${project.version}</composite.revision>
        <composite.partition>default</composite.partition>        
        <serverUrl>${oracleServerUrl}</serverUrl>        
        <user>${oracleUsername}</user>
        <password>${oraclePassword}</password>
        <overwrite>true</overwrite>
        <forceDefault>true</forceDefault>
        <regenerateRulebase>false</regenerateRulebase>
        <keepInstancesOnRedeploy>false</keepInstancesOnRedeploy>
        
        <!-- These parameters are used by the test goal 
         if you are using the sca-test (test) goal, you need to uncomment the following
             line and point it to your jndi.properties file. --> 
             
        <!--<jndi.properties.input>UNDEFINED</jndi.properties.input>-->
        <scatest.result>${scac.output.dir}/testResult</scatest.result>
        <!--  input is the name of the composite to run test suties against -->
        <input>${project.artifactId}</input>
        
        <!--<scac.ant.buildfile>${env.MW_HOME}/soa/bin/ant-sca-compile.xml</scac.ant.buildfile>
        <sca.ant.testfile>${env.MW_HOME}/soa/bin/ant-sca-test.xml</sca.ant.testfile>
        -->
        
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>com.oracle.soa.plugin</groupId>
                <artifactId>oracle-soa-plugin</artifactId>
                <version>12.1.3-0-0</version>
                <configuration>
                    <compositeName>${project.artifactId}</compositeName>
                    <composite>${scac.input}</composite>
                    <sarLocation>${scac.output.dir}/sca_${project.artifactId}_rev${composite.revision}.jar</sarLocation>
                    <serverUrl>${serverUrl}</serverUrl>
                    <appHome>../../../</appHome>
                    <!-- Note: compositeRevision is needed to package, revision is needed to undeploy -->
                    <compositeRevision>${project.version}</compositeRevision>
                    <revision>${composite.revision}</revision>
					<scacInputDir>${scac.input.dir}</scacInputDir>
					<user>${user}</user>
                    <password>${password}</password>                    
                    <input>${input}</input> 
                </configuration>
                 <!-- extensions=true is needed to use the custom sar packaging type -->
                <extensions>true</extensions>
            </plugin>
        </plugins>
    </build>
</project>

You should add

<appHome>../../../</appHome>

to you plugin. This will have to point to the place where your application is located and thus where your .adf/META-INF/adf-config.xml is also. In my case, that is 3 folders up when I do a checkout of my svn. You also have to fix ${project.version} as it will by default generates a 1.0-SNAPSHOT version but it will look for a 1.0 to deploy.

After you have done all this lets create a Jenkins job called SOA – Service and run the following maven command:

clean pre-integration-test -DoracleServerUrl=${soaServername} -DoracleUsername=${soaUsername} -DoraclePassword=${soaPassword}

Don’t forget to set soamds.apps.home=${workspace}\GedeeldeObjecten as environment variable and to also checkout the application.jws including .adf/META-INF/adf-config.xml. Now lets give the job a spin:

Started by user Hugo Hendriks
[EnvInject] - Loading node environment variables.
Building in workspace D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace
Cleaning local Directory .
Checking out svn://hendriksh@u10023o/mmi/trunk/services at revision '2015-02-18T20:23:09.684 +0100'
A         VGZ-SOA-Application.jws
 U        .
At revision 927
Cleaning local Directory .adf
Checking out svn://hendriksh@u10023o/mmi/trunk/services/.adf at revision '2015-02-18T20:23:09.684 +0100'
A         META-INF
A         META-INF\adf-config.xml
At revision 927
Cleaning local Directory GedeeldeObjecten/apps
Checking out svn://hendriksh@u10023o/mmi/trunk/OSB/GedeeldeObjecten/apps at revision '2015-02-18T20:23:09.684 +0100'
A         services\HelloService
A         services\HelloService\1.0
A         services\HelloService\1.0\HelloService.xsd
A         services\HelloService\1.0\HelloService.wsdl
A         services\HelloService\1.0\cdm.xsd
 U        .
At revision 927
Cleaning local Directory HelloService_v1.0/soa/HelloService_v1.0
Checking out svn://hendriksh@u10023o/mmi/trunk/services/HelloService_v1.0/soa/HelloService_v1.0 at revision '2015-02-18T20:23:09.684 +0100'
A         HelloService_v1.0.jpr
A         SOA
A         SOA\composite.xml
A         SOA\Schemas
A         SOA\Events
A         SOA\Mediators
A         SOA\Mediators\HelloMediator.mplan
A         SOA\measurements.xml
A         SOA\Transformations
A         SOA\WSDLs
A         SOA\testsuites
A         SOA\testsuites\fileList.xml
A         pom.xml
 U        .
At revision 927
Cleaning local Directory Build
Checking out svn://hendriksh@u10023o/mmi/trunk/build at revision '2015-02-18T20:23:09.684 +0100'
A         build-env-TST.properties
A         build-env-ONT.properties
At revision 927
no change for svn://hendriksh@u10023o/mmi/trunk/services/.adf since the previous build
no revision recorded for svn://hendriksh@u10023o/mmi/trunk/services/HelloService_v1.0/soa/HelloService_v1.0 in the previous build
no change for svn://hendriksh@u10023o/mmi/trunk/build since the previous build
[EnvInject] - Injecting environment variables from a build step.
[EnvInject] - Injecting as environment variables the properties file path 'Build/build-env-ONT.properties'
[EnvInject] - Variables injected successfully.
[EnvInject] - Injecting as environment variables the properties content 
soamds.apps.home=${workspace}\GedeeldeObjecten

[EnvInject] - Variables injected successfully.
Parsing POMs
Modules changed, recalculating dependency graph
[HelloService_v1.0] $ D:\ProgramFiles\Java\jdk1.7.0_71/bin/java -cp D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-agent-1.5.jar;D:\ProgramFiles\Apache\apache-maven-3.2.3\boot\plexus-classworlds-2.5.1.jar;D:\ProgramFiles\Apache\apache-maven-3.2.3/conf/logging jenkins.maven3.agent.Maven31Main D:\ProgramFiles\Apache\apache-maven-3.2.3 D:\ProgramFiles\Apache\Tomcat8.0\webapps\jenkins\WEB-INF\lib\remoting-2.48.jar D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven31-interceptor-1.5.jar D:\tomcatfiles\.jenkins\plugins\maven-plugin\WEB-INF\lib\maven3-interceptor-commons-1.5.jar 58351
<===[JENKINS REMOTING CAPACITY]===>���channel started
Executing Maven:  -B -f D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\pom.xml clean pre-integration-test -DoracleServerUrl=[MY-SERVER] -DoracleUsername=[MY-USER] -DoraclePassword=[MY-PASSWORD]
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building HelloService 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ HelloService ---
[INFO] 
[INFO] --- maven-resources-plugin:2.7:resources (default-resources) @ HelloService ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\src\main\resources
[INFO] 
[INFO] --- oracle-soa-plugin:12.1.3-0-0:compile (default-compile) @ HelloService ---
[INFO] ------------------------------------------------------------------------
[INFO] ORACLE SOA MAVEN PLUGIN - COMPILE
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] ABOUT TO RUN oracle.soa.scac.ValidateComposite...
[INFO] compile: Executing: [cmd:[D:\ProgramFiles\Java\jdk1.7.0_71\bin\java, -Djava.protocol.handler.pkgs=oracle.mds.net.protocol|oracle.fabric.common.classloaderurl.handler|oracle.fabric.common.uddiurl.handler, oracle.soa.scac.ValidateComposite, D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\SOA//composite.xml, -level=1, -appHome=../../../]]
[INFO] Process being executed, waiting for completion.
[INFO] [exec] 2015-02-18 20:23:35.950/4.421 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/D:/ProgramFiles/Apache/apache-maven-repository/com/oracle/coherence/coherence/12.1.3-0-0/coherence-12.1.3-0-0.jar!/tangosol-coherence.xml"
[INFO] [exec] 2015-02-18 20:23:36.091/4.562 Oracle Coherence 12.1.3.0.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/D:/ProgramFiles/Apache/apache-maven-repository/com/oracle/coherence/coherence/12.1.3-0-0/coherence-12.1.3-0-0.jar!/tangosol-coherence-override-dev.xml"
[INFO] [exec] 2015-02-18 20:23:36.091/4.562 Oracle Coherence 12.1.3.0.0 <D5> (thread=main, member=n/a): Optional configuration override "/tangosol-coherence-override.xml" is not specified
[INFO] [exec] 2015-02-18 20:23:36.107/4.578 Oracle Coherence 12.1.3.0.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-config.xml" is not specified
[INFO] [exec] 2015-02-18 20:23:36.107/4.578 Oracle Coherence 12.1.3.0.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-builder-config.xml" is not specified
[INFO] [exec] 2015-02-18 20:23:36.107/4.578 Oracle Coherence 12.1.3.0.0 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
[INFO] [exec] 
[INFO] [exec] Oracle Coherence Version 12.1.3.0.0 Build 52031
[INFO] [exec]  Grid Edition: Development mode
[INFO] [exec] Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
[INFO] [exec] 
[INFO] [exec] 2015-02-18 20:23:36.247/4.718 Oracle Coherence GE 12.1.3.0.0 <Info> (thread=main, member=n/a): Created cache factory com.tangosol.net.DefaultConfigurableCacheFactory
[INFO] [exec] Feb 18, 2015 8:23:37 PM oracle.fabric.common.wsdl.SchemaManager isIncrementalBuildSupported
[INFO] [exec] INFO: XMLSchema incremental build enabled.
[INFO] [exec] Mediators/HelloMediator.mplan: warning: Assigning property/constant "concat("Hallo ", $in.payload/tns:HelloRequest/tns:Naam)" to element "$out.payload/tns:HelloResponse/tns:begroeting". Please make sure target is single leaf node, otherwise non-leaf node will contain only string value which may generate non-valid xml as per the xsd.
[INFO] compile: [cmd:[D:\ProgramFiles\Java\jdk1.7.0_71\bin\java, -Djava.protocol.handler.pkgs=oracle.mds.net.protocol|oracle.fabric.common.classloaderurl.handler|oracle.fabric.common.uddiurl.handler, oracle.soa.scac.ValidateComposite, D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\SOA//composite.xml, -level=1, -appHome=../../../]] exit code=0
[INFO] SOA COMPILE DONE
[INFO] 
[INFO] --- maven-resources-plugin:2.7:testResources (default-testResources) @ HelloService ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\src\test\resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.2:testCompile (default-testCompile) @ HelloService ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ HelloService ---
[INFO] No tests to run.
[JENKINS] Recording test results
[INFO] 
[INFO] --- oracle-soa-plugin:12.1.3-0-0:sar (default-sar) @ HelloService ---
[INFO] Building sar: D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\target\sca_HelloService_rev1.0.jar
[INFO] 
[INFO] --- oracle-soa-plugin:12.1.3-0-0:deploy (default-deploy) @ HelloService ---
[INFO] ------------------------------------------------------------------------
[INFO] ORACLE SOA MAVEN PLUGIN - DEPLOY COMPOSITE
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] setting user/password..., user=weblogic
Processing sar=D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0/target/sca_HelloService_rev1.0.jar
Adding sar file - D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\target\sca_HelloService_rev1.0.jar
INFO: Creating HTTP connection to host:[MY-SERVER], port:[MY-PORT]
INFO: Received HTTP response from the server, response code=200
---->Deploying composite success.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 21.328 s
[INFO] Finished at: 2015-02-18T20:23:41+01:00
[INFO] Final Memory: 27M/356M
[INFO] ------------------------------------------------------------------------
Waiting for Jenkins to finish collecting data
[JENKINS] Archiving D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\pom.xml to VGZ-SOA-Application/HelloService/1.0/HelloService-1.0.pom
[JENKINS] Archiving D:\tomcatfiles\.jenkins\jobs\SOA - Service\workspace\HelloService_v1.0\soa\HelloService_v1.0\target\sca_HelloService_rev1.0.jar to VGZ-SOA-Application/HelloService/1.0/HelloService-1.0.jar
channel stopped
Archiving artifacts
Finished: SUCCESS

Perfect! As a final we can make a job to run a unit test against the just deployed server. I already made a description of how to do this here.

Now for the multijob. This will be a Release-Service job which calls all of the above step in a specific order. In the end you can use the CopyArtifact plugin to copy all of the generated artifacts into the Release-Service job. You can then zip these. I’m using the promote plugin to authorize the upload of the zip into Nexus. So the job looks like this now:

Release-Service

The stars on the left side will tell you that these where builds which where promoted and ended up in Nexus. I’m using a windows batch command to upload the zip to Nexus like this:

mvn deploy:deploy-file -Durl=[MY-SERVER] -DrepositoryId=local-nexus -DgroupId=nl.cvgz.mmi.services -DartifactId=%servicenaamkort% -Dversion=%serviceversie% -Dpackaging=zip -Dfile=target/%servicenaam%.zip

And finally they will end up in Nexus then. A zip which will contain:

  • OSB/SharedObject.sbar
  • OSB/Service.sbar
  • OSB/Customizations. Directory with customizations files for DTAP
  • SOA/MDS.zip
  • SOA/Service.jar

Nexus

So this could possibly be a way to automatically build, deploy, test, package and release a whole service using Jenkins. Some issues which I haven’t solved yet:

  • It seems adding a customization file during the deploy of the SB component gives an error. Could be a bug though.
  • Deploying the MDS.zip to the soa server through maven. If you know how to do this….drop me a line please

The Oracle Maven repository available now!

For ages, software companies have had maven repositories to manage their artifacts. Using maven’s dependencies system, you can easily add new libraries to your project without needing to know which other libraries you needed and maven will even download them for you. So the times of adding a library…compiling…class-not-found…adding another library…compiling…next-class-not-found where over. Unless you needed some oracle libraries!

logo

Up to now, Oracle did not have a public repository so when we needed libraries, you had to download the libraries yourself and stick them into your local repository. Or use the tool provided by Oracle to do this for you see last part of this weblog. But with the coming of the 12C version where Oracle has gotten more Maven enabled, it would not be long before the Oracle maven repository would come to life and taataaaaaa! Oracle now has a maven repository: http://maven.oracle.com. You will have to register/login and accept the user terms before you can access the repository. The repository only holds release-level artifacts, such as 12.1.2 and 12.1.3 for now.

Lets see if it works connecting straight to the Oracle repository using maven. Download Maven 3.2.5 or later as only these versions are supported by the repository it seems. Go to settings.xml and add the oracle server to the servers section.

<servers>
  <server>
    <id>maven.oracle.com</id>
    <username>YOURUSERNAME</username>
    <password>YOURPASSWORD</password>
    <configuration>
      <basicAuthScope>
        <host>ANY</host>
        <port>ANY</port>
        <realm>OAM 11g</realm>
      </basicAuthScope>
      <httpConfiguration>
        <all>
          <params>
            <property>
              <name>http.protocol.allow-circular-redirects</name>
              <value>%b,true</value>
            </property>
          </params>
        </all>
      </httpConfiguration>
    </configuration>
  </server>

Now go to a 12C service bus project you have lying around and add the next section to your pom.xml:

<repositories>
	<repository>
		<id>maven.oracle.com</id>
		<releases>
			<enabled>true</enabled>
		</releases>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
		<url>https://maven.oracle.com</url>
		<layout>default</layout>
	</repository>
</repositories>
<pluginRepositories>
	<pluginRepository>
		<id>maven.oracle.com</id>
		<url>https://maven.oracle.com</url>
	</pluginRepository>
</pluginRepositories>

Now we have configured the Oracle repository for the project and in the maven config we have set the password. So lets do a mvn clean and see if the dowloads starts…..

D:\Amazon EC2\SVN\trunk\osb\HelloService_1.0>mvn clean
[INFO] Scanning for projects...
Downloading: https://maven.oracle.com/com/oracle/servicebus/sbar-project-common/12.1.3-0-0/sbar-project-common-12.1.3-0-0.pom
Downloaded: https://maven.oracle.com/com/oracle/servicebus/sbar-project-common/12.1.3-0-0/sbar-project-common-12.1.3-0-0.pom (0 B at 0.0 KB/sec)
Downloading: https://maven.oracle.com/com/oracle/servicebus/client/maven-metadata.xml
Downloaded: https://maven.oracle.com/com/oracle/servicebus/client/maven-metadata.xml (381 B at 0.2 KB/sec)
Downloading: https://maven.oracle.com/com/oracle/weblogic/weblogic-server-pom/maven-metadata.xml
...
Downloaded: https://maven.oracle.com/com/oracle/servicebus/core/module-common/maven-metadata.xml (393 B at 0.2 KB/sec)
Downloading: https://maven.oracle.com/com/oracle/servicebus/core/module-configfwk/maven-metadata.xml
Downloaded: https://maven.oracle.com/com/oracle/servicebus/core/module-configfwk/maven-metadata.xml (396 B at 0.1 KB/sec)
Downloading: https://maven.oracle.com/com/oracle/servicebus/core/module-kernel-api/maven-metadata.xml
Downloaded: https://maven.oracle.com/com/oracle/servicebus/core/module-kernel-api/maven-metadata.xml (397 B at 0.2 KB/sec)
[WARNING]
[WARNING] Some problems were encountered while building the effective model for nl.redrock:HelloService_1.0:sbar:1.0-SNAPSHOT
[WARNING] 'parent.relativePath' points at RR-ServiceBusApplication:RR-ServiceBusApplication instead of com.oracle.servicebus:sbar-project-common, please verify your project structure @ line 6, column 13
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building HelloService_1.0 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ HelloService_1.0 ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 09:58 min
[INFO] Finished at: 2015-01-14T20:00:20+01:00
[INFO] Final Memory: 9M/120M
[INFO] ------------------------------------------------------------------------
D:\Amazon EC2\SVN\trunk\osb\HelloService_1.0>

So perfect….it works!

The next thing we want to do is proxy the repository in your Nexus for example. We can add the server in Nexus but we can’t seem to browse it. After some looking around it seems a lot of repository managers can’t handle the authentication way of the Oracle repository and have to be patched. See a comment of Mark Nelson about this:

Hi Folks,

We are aware that some of you would want to proxy the Oracle Maven Repository with repository managers like Artifactory and Nexus. We have tested the three common ones, and found that they all need small patches to understand the authentication we are using. We are working with them to patch these issues. We will let you know the JIRA ID’s for these issues when they are available. If you want to help, you can upvote the issues, or if you are using commercial versions, you could contact your representative to let them know you want these patches.

Best regards,

Mark Nelson

So work in progress but definately a step in the right direction!

References:

Custom maven plugin example

Maven has a lot of custom plugins build by people all over the world so searching for them on the web is always a first but it could be that you can’t find what you are looking for. In that case you can always write your own plugin. It is quite simple. Start of my creating a maven project based on the maven-archetype-mojo. Fill in your artifactId en groupId and you are good to go. My plugin is going to be one that displays a custom text as ascii art. Quite useless but funny. My project setup looks like this:
ascii-art-project
As you can see I have only 1 class and 1 test class. Let’s look at the class where all the magic happens.

package nl.redrock.maven.plugins.misc;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

/**
 * Goal which shows a cool ascii art at start of the run
 * 
 * @goal ascii
 * 
 */
public class AsciiArtMojo extends AbstractMojo {

	/**
	 * The string to print in ascii art
	 * 
	 * @parameter property="developerName" default-value="${project.artifactId}"
	 */
	private String developerName;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {

		int width = 100;
		int height = 30;

		System.out.println("Building: \n");
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_RGB);
		Graphics g = image.getGraphics();
		g.setFont(new Font("Arial", Font.BOLD, 16));

		Graphics2D graphics = (Graphics2D) g;
		graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
				RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		graphics.drawString(developerName, 10, 20);

		for (int y = 0; y < height; y++) {
			StringBuilder sb = new StringBuilder();
			for (int x = 0; x < width; x++) {

				sb.append(image.getRGB(x, y) == -16777216 ? " " : "$");

			}

			if (sb.toString().trim().isEmpty()) {
				continue;
			}

			System.out.println(sb);
		}

	}

	/**
	 * For testing purpose
	 * 
	 * @param designerDirectory
	 */
	public void setDeveloperName(String aName) {
		this.developerName = aName;
	}

}

As you can see the class has to extend AbstractMojo and override the execute method. In the class comment you can set the goal name, ascii in my case. You can also declare parameters which can be set in your plugin configuration in the project which will use your plugin:

	/**
	 * The string to print in ascii art
	 * 
	 * @parameter property="developerName" default-value="${project.artifactId}"
	 */
	private String developerName;

After this you can write a simple testcase to see if your plugin works like intended. I just wrote a simple testcase using junit which looks like this:

package nl.redrock.maven.plugins.misc;

import junit.framework.TestCase;
import nl.redrock.maven.plugins.misc.AsciiArtMojo;

import org.junit.Test;

public class AsciiArtMojoTest extends TestCase {
	
	@Test
	public void testMojo() throws Exception{
		AsciiArtMojo mojo = new AsciiArtMojo();
		mojo.setDeveloperName("RedRock");
		mojo.execute();
	}
}

When i run this test you hopefully will see the following:


-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running nl.redrock.maven.plugins.misc.AsciiArtMojoTest
Building: 

           $$$$$$$$$                  $$  $$$$$$$$$                      $$                         
           $$$$$$$$$$                 $$  $$$$$$$$$$                     $$                         
           $$    $$$$                 $$  $$    $$$$                     $$                         
           $$      $$   $$$$$    $$$$$$$  $$      $$   $$$$$$    $$$$$$  $$  $$$$                   
           $$    $$$$  $$$$$$$  $$$$$$$$  $$    $$$$  $$$$$$$$  $$$$$$$  $$ $$$$                    
           $$$$$$$$$$  $$$ $$$  $$$$$$$$  $$$$$$$$$$  $$$$$$$$  $$$$$$$  $$$$$$                     
           $$$$$$$$$   $$$$$$$  $$$  $$$  $$$$$$$$$   $$$  $$$  $$$      $$$$$                      
           $$ $$$$$$   $$$$$$$  $$    $$  $$ $$$$$$   $$    $$  $$       $$$$$$                     
           $$   $$$$   $$$      $$$  $$$  $$   $$$$   $$$  $$$  $$$      $$$$$$                     
           $$    $$$$  $$$$$$$  $$$$$$$$  $$    $$$$  $$$$$$$$  $$$$$$$  $$  $$$                    
           $$     $$$$ $$$$$$$  $$$$$$$$  $$     $$$$ $$$$$$$$  $$$$$$$  $$  $$$                    
           $$     $$$$  $$$$$    $$$$$$$  $$     $$$$  $$$$$$    $$$$$   $$   $$$                   
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.09 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

The pom file for the plugin is also quite simple. Something like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>nl.redrock.maven.plugins.misc</groupId>
	<artifactId>ascii-art-plugin</artifactId>
	<packaging>maven-plugin</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Ascii Art plugin</name>

	<dependencies>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-plugin-api</artifactId>
			<version>3.1.1</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<type>jar</type>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

Ok, the plugin is done now. The only thing we want to do is to install it so maven can find it in my repository next time I want to use it in one of my projects. This can be accomplished quite easily by running mvn clean install.

Now let’s make a new maven project and see if the plugin runs like we want it to. I created a simple maven project with a pom which looks like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" 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>nl.redrock</groupId>
	<artifactId>plugintest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>plugintest</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>nl.redrock.maven.plugins.misc</groupId>
				<artifactId>ascii-art-plugin</artifactId>
				<version>1.0-SNAPSHOT</version>
				<executions>
					<execution>
						<phase>pre-clean</phase>
						<goals>
							<goal>ascii</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<developerName>${artifactId}</developerName>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Lets look at the build plugins. You can see I declared my just uploaded plugin here and set the phase in which it should run on pre-clean. I also declared the goal and in the configuration part, I filled the property which will be printed as ascii art. In my case this will be the artifactId from the pom file. Ok…..time to see if it all works. Run mvn clean. The outcome should look this:

[INFO] Scanning for projects...
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for nl.redrock:plugintest:jar:0.0.1-SNAPSHOT
[WARNING] The expression ${artifactId} is deprecated. Please use ${project.artifactId} instead.
[WARNING] 
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING] 
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING] 
[INFO] 
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building plugintest 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- ascii-art-plugin:1.0-SNAPSHOT:ascii (default) @ plugintest ---
Building: 

                     $$                      $$            $$                     $$                
                     $$                      $$            $$                     $$                
                     $$                                    $$                     $$                
           $$$$$$$   $$  $$    $$   $$$$$$$  $$  $$$$$$$$ $$$$$  $$$$$   $$$$$$$ $$$$$              
           $$$$$$$$  $$  $$    $$  $$$$$$$$  $$  $$$$$$$$ $$$$$ $$$$$$$  $$$$$$$ $$$$$              
           $$$$$$$$  $$  $$    $$  $$$$$$$$  $$  $$$$$$$$  $$   $$$ $$$  $$$ $$$  $$                
           $$$  $$$  $$  $$    $$  $$$  $$$  $$  $$$  $$$  $$   $$$$$$$  $$$$$$   $$                
           $$    $$  $$  $$    $$  $$    $$  $$  $$    $$  $$   $$$$$$$  $$$$$$$  $$                
           $$$  $$$  $$  $$$  $$$  $$$  $$$  $$  $$    $$  $$   $$$        $$$$$  $$                
           $$$$$$$$  $$  $$$$$$$$  $$$$$$$$  $$  $$    $$  $$$$ $$$$$$$  $$$ $$$  $$$$              
           $$$$$$$$  $$  $$$$$$$$  $$$$$$$$  $$  $$    $$  $$$$ $$$$$$$  $$$$$$$  $$$$              
           $$$$$$$   $$  $$$$$$$$   $$$$$$$  $$  $$    $$  $$$$  $$$$$   $$$$$$$  $$$$              
           $$                      $$$ $$$$                                                         
           $$                      $$$$$$$$                                                         
           $$                      $$$$$$$                                                          
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ plugintest ---
[INFO] Deleting D:\Workspace\Java\plugintest\target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.453 s
[INFO] Finished at: 2014-03-15T00:24:44+01:00
[INFO] Final Memory: 6M/154M
[INFO] ------------------------------------------------------------------------

Mission accomplished!