Using JMX to list installed 12C OSB and SOA projects including versions using Java

Lately we where looking for a way to easily list all the services (OSB and SOA) installed on our DTAP machines. A CMDB works as well as it is maintained. As this is tedious and manual labour, we all know from experience that the CMDB is never fully correct. So how do we know then what is installed where? We can open the EM and the sbconsole and have a look there but wouldn’t it be more fancy if we would just select an environment, run some script and it would give you a list of the services?

Ok, how do we obtain al this information then? Weblogic has a JMX (Java Management eXtensions) interface which we can use to access mbeans which should hold all the information we need. Let’s start of with our SOA server first. You can use WLST but as I’m a Java fan, I will use Java. Starting of with a simple Maven project I first need to setup a connection to a server, lookup the right mbean which can give me a list of the installed services, access it and fill some result. Let’s first init the connection and the use the connection to lookup the right mbean.

   /**
     * Initialize the connection with the server
     * @param hostname the host
     * @param port the port
     * @param username the username
     * @param password the password
     * @return a JMXConnector
     * @throws IOException
     * @throws MalformedURLException
     */
    public 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);
    }

    /**
     * List the SOA services
     *
     * @throws Exception
     */
    public static List<DeployedService> getSOA(String host, int port, String username, String password) throws Exception {

        List<DeployedService> result = new ArrayList();

        JMXConnector connector = initConnection(host, port, username, password);
        // get mbean connection
        MBeanServerConnection mbconn = connector.getMBeanServerConnection();

        ObjectName queryObject = new ObjectName("oracle.soa.config:Location=soa_ms01,name=soa-infra,j2eeType=CompositeLifecycleConfig,Application=soa-infra,*");
        Set<ObjectName> queryObjectName = mbconn.queryNames(queryObject, null);
        Iterator iterator = queryObjectName.iterator();

        while (iterator.hasNext()) {
            ObjectName compositeObjectName = (ObjectName) iterator.next();

            CompositeData[] composites = (CompositeData[]) mbconn.getAttribute(compositeObjectName, "DeployedComposites");
            for (CompositeData composite : composites) {
                String dn = (String) composite.get("DN");
                Boolean isDefault = (Boolean) composite.get("isDefault");
                String state = (String) composite.get("state");
                String mode = (String) composite.get("mode");

                if (isDefault && state.equalsIgnoreCase("on") && mode.equalsIgnoreCase("active")) {
                    DeployedService service = new DeployedService();
                    service.setName(dn.substring(dn.indexOf('/') + 1, dn.indexOf('!')));
                    service.setVersion(dn.substring(dn.indexOf('!') + 1, dn.indexOf('*')));
                    if (!result.contains(service)) {
                        result.add(service);
                    }
                }
            }
        }
        connector.close();
        return result;
    }

As you can see the CompositeData data has all of the data we need. We can check if the composite is Active and if it is Default and we can extract the version from the DN. As for dependencies, I suspected I would need the wlfullclient and probably some other jars. Netbeans neatly suggested the dependencies for me from my local maven repository and it seemed I only needed 1 which was soa-infra-mgmt. So my first dependcies where:

        <dependency>
            <groupId>com.oracle.weblogic</groupId>
            <artifactId>wlfullclient</artifactId>
            <version>12.1.3</version>
        </dependency>
        <dependency>
            <groupId>com.oracle.soa</groupId>
            <artifactId>soa-infra-mgmt</artifactId>
            <version>12.1.3-0-0</version>
            <type>jar</type>
        </dependency>

Next but is the OSB. This was a bit more tricky as the OSB doesn’t support versioning. We worked around this issue by adding version info in the description field of the project. See here for an explanation. So it was kind of obvious there was no mbean to retrieve this info. The best way I found was just to export all the project, unzip them and just loop through all the projects and check their description field. The code looks like this:

    /**
     * List all the OSB services
     *
     * @throws Exception
     */
    public static List<DeployedService> getOSB(String host, int port, String username, String password) throws Exception {

        List<DeployedService> result = new ArrayList();

        // get the jmx connector
        JMXConnector connector = initConnection(host, port, username, password);

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

        // get domain service mbean. This is the topmost mbean
        DomainRuntimeServiceMBean domainService = (DomainRuntimeServiceMBean) MBeanServerInvocationHandler.
                newProxyInstance(mbconn, new ObjectName(DomainRuntimeServiceMBean.OBJECT_NAME));

        // Obtain MBean for peforming read only operations. Notice that the name
        // of the mbean for the core data does not contain any session name.
        ALSBConfigurationMBean alsbCore = (ALSBConfigurationMBean) domainService.findService(ALSBConfigurationMBean.NAME, ALSBConfigurationMBean.TYPE, null);

        Set<Ref> refs = alsbCore.getProjects();
        byte[] contentsProj = alsbCore.exportProjects(new ArrayList(refs), null);

        //write the projects to the target directory
        FileOutputStream fos = new FileOutputStream("target/export-sbconfig.jar");
        fos.write(contentsProj);
        fos.close();

        //unzip the zip file
        File unzipDir = new File("target/export-sbconfig");
        FileUtils.unZipIt("target/export-sbconfig.jar", unzipDir);

        //loop through them and read the value
        File[] files = unzipDir.listFiles();
        DeployedService service;
        for (File file : files) {
            //name of the directory
            String name = file.getName();
            if (name.contains("_")) {
                String description = XMLUtils.readDescription(file.getAbsolutePath() + "/_projectdata.LocationData");
                service = new DeployedService();
                service.setNaam(name);
                service.setVersie(description);
                result.add(service);
            }
        }

        //delete the zip and the unzip dir
        FileUtils.delete(unzipDir);
        FileUtils.delete(new File("target/export-sbconfig.jar"));

        connector.close();
        return result;
    }

As you can see I export the zip file to the target directory, unzip it there and then loop through them and checking the _projectdata.LocationData file. What about the dependencies here? Netbeans found the obvious ones for me which where module-kernel-api and module-configfwk both in com.oracle.servicebus.core. So

        <dependency>
            <groupId>com.oracle.servicebus.core</groupId>
            <artifactId>module-kernel-api</artifactId>
            <version>12.1.3-0-0</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>com.oracle.servicebus.core</groupId>
            <artifactId>module-configfwk</artifactId>
            <version>12.1.3-0-0</version>
            <type>jar</type>
        </dependency>

Well that seemed about right. I tried to give it a spin but unfortunately I ran into a ClassNotFoundException……..specificaly com.bea.wli.sb.management.configuration.DelegatedALSBConfigurationMBean. It seemed I needed something else at runtime. After some Googling, I found out the jar I needed was sb-kernel-impl.jar but I couldn’t find it anywhere on the 12C installation. In a 11G installation, it was under OSB_DOMAIN/lib so I spinned up an old 11G VM and grabbed the jar and uploaded it into my maven repo. Again, give it a try…..and there we go….it work. Next I tweaked it a bit so I could start it from Jenkins and it would output the list in easy readable format. The result look like this.

overview

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: Generating a JSON Web Token (JWT) in OSB

“JSON Web Token (JWT) is a JSON-based open standard (RFC 7519) for creating access tokens that assert some number of claims. For example, a server could generate a token that has the claim “logged in as admin” and provide that to a client. The client could then use that token to prove that it is logged in as admin. The tokens are signed by the server’s key, so the client is able to verify that the token is legitimate. The tokens are designed to be compact, URL-safe and usable especially in web browser single sign-on (SSO) context. JWT claims can be typically used to pass identity of authenticated users between an identity provider and a service provider, or any other type of claims as required by business processes. The tokens can also be authenticated and encrypted.” Wikipedia

jwt

I guess the above text says it all. I won’t be going into all the JWT details as you can read most of it online on JWT.io. For my current customer I had to interface with a SaaS product which used JWT as authentication measure. There are quite some libraries for Java which you can use to generate tokens. To mention a few:

  • com.auth0.java-jwt
  • org.bitbucket.b_c.jose4j
  • com.nimbusdsnimbus.jose-jwt
  • io.jsonwebtoken.jjwt

Easy as I thought, I grabbed the most obvious one I could find and created a simple class which could create a token for me. Sounded fairly easily but the used jar also had it’s dependencies which of course had to be available in Weblogic also for it to work. This is where the problem started as the dependencies seem to cause some class loading issues. I tried another JWT implementation but this also seem to cause some issues. After some googling I found that Oracle also had JWT support looking at the page. The only thing I really couldn’t find very quickly was which jars I needed and where they where….

After some poking around in my Oracle_Home I found the right jars though. The ones you need to create a token are:

  • osdt_cert.jar
  • osdt_core.jar
  • osdt_restsec.jar
  • jackson-core-asl-1.1.1.jar
  • jackson-mapper-asl-1.1.1.jar

The first 3 are located in your OracleHome\oracle_common\modules\oracle.osdt_12.1.3 folder. The 2 jackson ones you can just grab of the internet
These jars contain all the classes you need to create a token and verify it in your Java development environment. The first three are already on your Weblogic classpath. The two Jackson also seem to be somewhere on the server although the documentation makes you think otherwise but I didn’t need to put the 2 Jackson libraries somewhere on the server to make it work.

To create a token you can just use the following code for example:

package nl.redrock.jwt;

import oracle.security.restsec.jwt.*;

public class JWTGenerator {
    
    
    public static String generateJWT(String aCode, String aAmount, String aKey) throws Exception {
        
        String result = null;
        
        JwtToken jwtToken = new JwtToken();
        //Fill in all the parameters- algorithm, issuer, expiry time, other claims etc
        jwtToken.setAlgorithm(JwtToken.SIGN_ALGORITHM.HS512.toString());
        jwtToken.setType(JwtToken.JWT);
        jwtToken.setClaimParameter("Amount", aAmount);
        jwtToken.setClaimParameter("Code", aCode);
        // Get the private key and sign the token with a secret key or a private key
        result = jwtToken.signAndSerialize(aKey.getBytes());
        return result;
    }
}

To call the generateJWT class, just create a custom xquery lib file…in my case custom-redrock-xquery.xml which looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
<xpf:xpathFunctions xmlns:xpf="http://www.bea.com/wli/sb/xpath/config">
	<xpf:category id="RedRock Custom Functions">
		<xpf:function>
			<xpf:name>generateJWT</xpf:name>
			<xpf:comment>Generate a JSON Web Token based on inputs</xpf:comment>
			<xpf:namespaceURI>http://www.redrock.nl/soa/xpath</xpf:namespaceURI>
			<xpf:className>nl.redrock.jwt.JWTGenerator</xpf:className>
			<xpf:method>java.lang.String generateJWT(java.lang.String, java.lang.String, java.lang.String)</xpf:method>
			<xpf:isDeterministic>false</xpf:isDeterministic>
			<xpf:scope>Pipeline</xpf:scope>
			<xpf:scope>SplitJoin</xpf:scope>
		</xpf:function>
	</xpf:category>
</xpf:xpathFunctions>

Stick this file under OracleHome\osb\config\xpath-functions along with the jar-file which contains your utility class and dont’t forget the osdt_restsec.jar as it is a dependency which seems to be needed at deploytime. Then start JDev up and in XQuery Expression builder popup you should now see the custom xquery.

xqueryexpressionbuilder

Now just deploy you project to the servicebus and run a test. If you enable execution tracing you will see that the assign works and we get a token.

token

Next select the token and go to JWT.io and check if it verifies:

jwtcheck

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

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.

OFM 12C: An example of the Business Rule Engine with Java facts

One of the components of the Oracle SOA Suite is the Business Rule Engine. The Oracle Business Rules engine….and I quote ‘allows the externalization of specific business logic and
parameters. Business analysts can easily define, update, and manage key parameters and decision trees that are likely to change based on business evolution (for instance discount levels, credit rates, etc.) without having to involve IT and developers.

This all sounds pretty nice but I haven’t seen it implemented anywhere yet but lately I have been working for a customer which has a good case to actually use the BRE so I had time look into it. In this blog I will show you a basic way to use the BRE using a decisions table and with the use of some custom Java facts.

A simple explanation: We have persons who wants to insure themselves from a certain date with a certain type of insurance. We are going to use the BRE to determine whether they are allowed to do this. We are going to use a decision table instead of if-then-else structures and in the decision table we are going to use java facts which we created to make certain functions more easy to define.

Lets start of with a simple SOA project. I created a simple schema with a input and output.

<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:rr="http://nl.redrock-it"
            targetNamespace="http://nl.redrock-it" elementFormDefault="qualified">
  <xsd:element name="breExampleRequest">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="dateOfBirth" type="xsd:date"/>
        <xsd:element name="startDate" type="xsd:date"/>
        <xsd:element name="insuranceType">
          <xsd:simpleType>
            <xsd:restriction base="xsd:string">
              <xsd:enumeration value="Basic"/>
              <xsd:enumeration value="Better"/>
              <xsd:enumeration value="Best"/>
            </xsd:restriction>
          </xsd:simpleType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="breExampleResponse">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="result" type="xsd:boolean"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Lets add a Mediator to the composite and expose it as a web service.

createMediator

Now lets create the BRE component. Drag the Business Rule into the composite. Input a name and select the input and output where you can select the request and response we have previously defined in the insurance.xsd and press OK.

createBR

If you edit the Business Rule you have different tabs.

BRtabs

Settings has some general settings as expected. The Facts tabs hold the request and response data structures which you will be using. These should already have the breExampleRequest and breExampleResponse in there. Also the checkbox visible should have been checked as we are going to use them.

BRfacts

In the Globals tab we can define globals which we might be using. We are going to define here the AdultAge.

BRglobal

Now lets create the actual ruleset. Go to the generated RuleSet1. Rename the RuleSet1 to InsuranceRuleSet. Then add a DecisionTable.

BRruleset

When you create the DecisionTable it opens automatic. A decision table is like a matrix. You have Conditions which together with Rules result into Actions. Lets insert a Condition first.

BRCondition

Now select the InsuranceType from the breExampleRequest.

BRConditionConfig

After this we can input the possible value which will be “Basic”. Now lets attach an action to it.

BRAction

Double-click the action and the popup opens. Here we can select from the facts the breExampleResponse and select the fact its parameterized.

BRActionConfig

Now press OK. Then add a value to the action which will be the simple boolean value “true”.

BRActionValue

Now we have 1 Rule but we want 3 so we at least can see the difference which different inputs so lets create an two extra Rules with the following values:

R1R2R3

So basically this ruleset should return “true” when I pick the insurance of type “Basic” and “false” if pick the insurance of type “Better” or “Best”. Before we connect it all together we have to define the interface for the BusinessRule component on the Decision Functions tab. Here we will configure the webservice interface which we will use to call the business rule. Go to the Decisions Functions tab and double click on the already created decision function. In the next popup you can define the name, the name of the operation of the webservice and the input and output. Also make sure the ruleset is selected on the Rule Sets & Decision Functions tab.

DecisionFunction

Now connect the mediator to the business rule. As you can see the BusinessRule component as an interface to which we can connect.

connecting

Make sure you map the input and output in the mediator.

Mapping

Now that it is all hooked up together, lets deploy it and give it a spin. I use SoapUI to call the service and lets see what happens.

SoapUITest

As you can see, we get TRUE for the ‘Basic’ insurance but FALSE for the ‘Better’ and ‘Best’ option. So now we have seen how to make use of a simple decision table. In a business environment, you probably want some more complex functions in the decision table. There are possibilities to make use of functions but from what I have seen, these are quite limited and not very clear on their usages. Another option is to write functions yourself using Java. You create a jar file which contain classes with your helper functions. I will demonstrate below how this works.

First create a simple class with a helper function. I created a simple class with a static function which determines if a person is an adult on a certain date. This will look like this:

package nl.redrock.bre;

import java.util.Calendar;
import javax.xml.datatype.XMLGregorianCalendar;

/**
 *
 * @author Hugo Hendriks
 */
public class Insurance {
    
    /**
     * Check whether the person is already an adult at the startDate
     * @param aStartDate the starting date
     * @param aBirthDate the day of birth
     * @param aAdultAge the legal age a person becomes an adult
     * @return true or false
     */
    public static boolean isAdultAtDate(XMLGregorianCalendar aStartDate, XMLGregorianCalendar aBirthDate, int aAdultAge){
     
        boolean result = false;
        
        Calendar birthdayLegalAge = aBirthDate.toGregorianCalendar();
        birthdayLegalAge.add(Calendar.YEAR, aAdultAge);
        
        if(birthdayLegalAge.before(aStartDate.toGregorianCalendar())){
            result = true;
        }
        return result;
    }
    
}

Keep in mind that xsd:date is tranformed to an XMLGregorianCalendar object. As a good practice write a JUnit test for it to check if it works as desired and build a jar. I am using maven so this would be the outcome.

Scanning for projects...

Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
                                                                        
------------------------------------------------------------------------
Building BRE-Test 1.0
------------------------------------------------------------------------

--- maven-clean-plugin:2.5:clean (default-clean) @ BRE-Test ---
Deleting D:\Amazon EC2\SVN\trunk\java\BRE-Test\target

--- maven-resources-plugin:2.6:resources (default-resources) @ BRE-Test ---
Using 'UTF-8' encoding to copy filtered resources.
skip non existing resourceDirectory D:\Amazon EC2\SVN\trunk\java\BRE-Test\src\main\resources

--- maven-compiler-plugin:2.5.1:compile (default-compile) @ BRE-Test ---
Compiling 1 source file to D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\classes

--- maven-resources-plugin:2.6:testResources (default-testResources) @ BRE-Test ---
Using 'UTF-8' encoding to copy filtered resources.
skip non existing resourceDirectory D:\Amazon EC2\SVN\trunk\java\BRE-Test\src\test\resources

--- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ BRE-Test ---
Compiling 1 source file to D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\test-classes

--- maven-surefire-plugin:2.12.4:test (default-test) @ BRE-Test ---
Surefire report directory: D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running nl.redrock.bre.InsuranceTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.098 sec

Results :

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


--- maven-jar-plugin:2.4:jar (default-jar) @ BRE-Test ---
Building jar: D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\BRE-Test-1.0.jar
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 4.922 s
Finished at: 2015-01-09T12:17:07+01:00
Final Memory: 13M/126M
------------------------------------------------------------------------

Now I have a jar containing the class which I want to use but how do I get in into my designtime environment? Very simple…just drop the jar file into the \SOA\.rulesdesigner\jaxb_classes folder….in my case BreTestProject\SOA\.rulesdesigner\jaxb_classes. Now lets go back to the Business Rule component. Go to the Facts tab and the Java Fact tab below and click Add.

AddJavaFact

In the popup select Add and select the .jar file we have just copied. When selected, the tree below should display the classes. Select the Insurance class and click OK.

SelectJar

You should see the class now in the list of Java facts. Now for the magic. Go to your decision table and add a new condition. Double click it and click the calculator icon on the top right. In the popup select the method from the Functions tab and select the right input parameters from the variables tab. We also put in there the AdultAge from the Constant tab we had defined before.

JavaFactCondition

The table will look like this now.

JavaFactDT

Now compile and deploy again. Don’t forget to add the jar into the /lib directory and restart your domain. When we run our SoapUI test you will see it also takes the Java check in the decision table into account.

Down the line the business rule component can help you, by putting some business logic into your middleware layer which can be edited through the soa-composer. The Java facts work quite nicely when the IDE is stable and the decision table works as it should. Sorting certain business functionality in the decision table instead of the decision tables takes some head twisting.

Thing to take into consideration:

  • There is a memory leak in the DecisionTable IDE editor. When you open it, the table will start to eat resources and eventually crash especially when you have a lot of columns and rows. There is a patch available but will also introduce a new bug which crashes the IDE completely! Un-patching using opatch seemed to get rid of the crash and of the memory leak.
  • There are classes being generated when you define your interface. If you want to change the interface later on, you have to delete and create the classes again or else you will run into errors here.
  • The BRE component has it’s fits. Sometimes you will have strange glitches or unexplainable behavior. I think this comes to the fact that not many customers use it so it doesn’t receive much love from Oracle :)

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!

Get numbers from Scrumwise through API

As you have seen from my previous posts, I have been using Scrumwise to help me facilitate Scrum in my projects. The think which I am missing but is on the wish-list is reporting. As teamlead I had to report to projectmanagers about progress, hours burned etc… All these things I can get from Scrumwise through its API. See https://www.scrumwise.com/api.html .

To get this done I wrote a simple Java program to get the a nice up-to-date printout of the stats. The first thing is you need to generate an API key in Scrumwise and get the code from there. Is this next bit I will show you how to connect to Scrumwise. It looks like this:

/**
 * Connect to Scrumwise and get all the scrumwise details in a JSONObject
 * 
 * @return JSONObject the result of the call
 * @throws Exception
 */
private static JSONObject callScrumwise() throws Exception {
		JSONObject result = null;
		// Setup proxy if needed
	//Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(
	//		PROXY_IP, PROXY_PORT));
	// Setup connection
	URL url = new URL("https://api.scrumwise.com/service/api/v1/getData");
	HttpURLConnection connection = (HttpURLConnection) url
			.openConnection();
	connection.setRequestMethod("POST");
	connection.setDoOutput(true);
		// setup credentials
	String loginPassword = "USERNAME:API-KEY";
	// Overwrite the setting or else it will cut of the encoded password
	String encoded = new sun.misc.BASE64Encoder() {
		@Override
		protected int bytesPerLine() {
			return 9999;
		}
	}.encode(loginPassword.getBytes());
	connection.setRequestProperty("Authorization", "Basic " + encoded);
	// setup POST parameters
	String urlParameters = "projectIDs=null&includeProperties=Project.backlogItems,BacklogItem.timeEntries, BacklogItem.tasks, Task.timeEntries, Data.persons";
		DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
	wr.writeBytes(urlParameters);
	wr.flush();
	wr.close();
		int responseCode = connection.getResponseCode();
	System.out.println("\nSending 'POST' request to URL : " + url);
	System.out.println("Post parameters : " + urlParameters);
	System.out.println("Response Code : " + responseCode);
		BufferedReader in = new BufferedReader(new InputStreamReader(
			connection.getInputStream()));
	String inputLine;
	StringBuffer response = new StringBuffer();
		while ((inputLine = in.readLine()) != null) {
		response.append(inputLine);
	}
	in.close();
		// proces result
	result = (JSONObject) JSONSerializer.toJSON(response.toString());
	return result;
	}

After that I loop through the items and pick out the backlogitems I need per project. I named the backlog items following the next convention: <project-code> <project-name>.

//project MyProject
private static String MyProject = "131206 MyProject: ";
//project MyProject backlog items
private static List<String> myprojectBacklogItems = new ArrayList<String>(Arrays.asList("Design", "Develop", "Test", "Coordinate"));

After this I call my generic method which prints me all the backlogitems with their hours and status. This looks something like this:

/**
 * Print time per user
 * @param aJson
 * @param aName
 */
private static void printUsedTimePerUser(JSONObject aJson, String aName){
	
	Map<String, Integer> result = new HashMap<String, Integer>();
	
	JSONArray projects = (JSONArray) aJson.getJSONObject("result").getJSONArray("projects");
	//loop through all projects
	for (int i = 0; i < projects.size(); i++) {
			JSONObject project = projects.getJSONObject(i);
		JSONArray backlogitems = project.getJSONArray("backlogItems");
		
		//loop through all backlogitems
		for (int j = 0; j < backlogitems.size(); j++) {
			JSONObject backlogItem = backlogitems.getJSONObject(j);
			//filter only the mosaic backlogitems
			if (backlogItem.getString("name").startsWith(aName)) {
				//System.out.printf("\n=====================================================================================================================================\n");
				// get hours under backlogitem
				JSONArray timeEntries = backlogItem.getJSONArray("timeEntries");
				for (int k = 0; k < timeEntries.size(); k++) {
					JSONObject timeEntry = timeEntries.getJSONObject(k);
					int backlogTimeEntry = timeEntry.getInt("usedTime");
					String person = timeEntry.getString("personID");
					Integer usedTime = result.get(person);
					if(usedTime == null){
						result.put(person, new Integer(backlogTimeEntry));	
					}
					else{
						result.put(person, usedTime + backlogTimeEntry);
					}
				}
					// get tasks under backlogitem
				JSONArray tasks = backlogItem.getJSONArray("tasks");
				for (int l = 0; l < tasks.size(); l++) {
					JSONObject task = tasks.getJSONObject(l);
					Iterator<JSONObject> taskTimeIter = task.getJSONArray("timeEntries").iterator();
					while (taskTimeIter.hasNext()) {
						JSONObject timeEntry = taskTimeIter.next();
						String person = timeEntry.getString("personID");
						int taskTimeEntry = timeEntry.getInt("usedTime");
						//System.out.println("Person " + person + " wrote " + taskTimeEntry + " hours on " + task.getString("name"));
						Integer usedTime = result.get(person);
						if(usedTime == null){
							result.put(person, new Integer(taskTimeEntry));	
						}
						else{
							result.put(person, usedTime + taskTimeEntry);
						}
						
					}
				}
			}
		}
	}
System.out.println(result.toString());
}

In the end….when you run all this you will get a simple output of the all the items which will look something like this:

Sending ‘POST’ request to URL : https://api.scrumwise.com/service/api/v1/getData
Post parameters : projectIDs=null&includeProperties=Project.backlogItems,BacklogItem.timeEntries, BacklogItem.tasks, Task.timeEntries, Data.persons
Response Code : 200

MyProject: hours per item

=================================================================================================================================
Subject | 131206 MyProject: Design                                                                   = 99  uur | Sprint completed
Task | Design Database                                                                               = 99  uur | Done

=================================================================================================================================
Subject | 31206 MyProject: Develop                                                                   = 31  uur | In progress
Task | Create proxy service                                                                          = 11  uur | Done
Task | Create business service                                                                       = 4   uur | Done
Task | Create xquery                                                                                 = 4   uur | In progress
Task | Import WSDL's                                                                                 = 6   uur | Done
Task | Junit test                                                                                    = 6   uur | In progress

…….etc

As you can see I haven’t really spend a lot of time optimizing code etc as i needed something quick and dirty and this does the job. Still work in progress but it will give you an idea of what is possible.