Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

Spring Boot

Spring Boot is changing the world of Java development with Spring. It is taking away much of the pain that was involved with configuring applications. This is great when you are trying to create what they do by default. They create executable jars by default. What do you have to do if you need to create a WAR file to run on a Tomcat by someone else.

POM file

Let’s start with our project’s pom.xml file. Here we need to define the standard Spring Boot parent starter and also include include the Spring Boot Web starter. The Spring Boot Web starter also includes the Spring Boot Tomcat starter. In our case, we do not want it packaged inside our war because our war file will be deployed on a Tomcat server directly. We don’t have to roll our own. To change this behavior, we include the Spring Boot tomcat starter explicitly and set it’s scope to provided. We also must specify the packaging of our project to be a war.

<?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>com.javaninja.springboot</groupId>
    <artifactId>springboot-web</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Java Config

Normally the Java Configuration contains a main method because that is how the program will be launched. In this case, we do not need a main method and instead we need to provide a configure method to go along with the SpringBootServletInitializer that we extend.

package com.javaninja.springboot.web;


import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;


@SpringBootApplication
public class WebApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(WebApplication.class);
    }
}

Example Controller

That is all of the configuration that we need to make this work as a normal Spring MVC controller. To test our functionality, we need a very simple controller, such as the following,.

package com.javaninja.springboot.web;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class ExampleController {

    private Logger log = LoggerFactory.getLogger(getClass());

    @GetMapping("/example/boolean")
    public Boolean exampleBoolean() {
        log.info("Eureka!");
        return true;
    }
}

Test the controller

Let’s create a very simple test using the Spring Boot Test starter. This brings in Junit and MockMvc among other useful APIs.

package com.javaninja.springboot.web;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.assertj.core.api.Assertions.fail;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


/**
 * @author norris.shelton
 */
@RunWith(SpringRunner.class)
@WebMvcTest(ExampleController.class)
public class TestExampleController {

    @Autowired
    private MockMvc mvc;

    @Test
    public void testExampleBoolean() {
        try {
            mvc.perform(get("/example/boolean")
                            .accept(MediaType.APPLICATION_JSON))
               .andExpect(status().isOk())
               .andExpect(content().string("true"));
        } catch (Exception e) {
            fail(e.toString());
        }
    }
}

The test run perfectly and produces the following logging and a nice pretty green bar.

2017-07-10 18:36:06.218  INFO 7499 --- [           main] c.j.springboot.web.ExampleController     : Eureka!

July 11th, 2017

Posted In: Maven, MockMVC, Spring, Spring Boot, Spring MVC, Test Driven Development, Unit Tests

Tags: , ,

Leave a Comment

The Maven Checkstyle Plugin is very useful to identity source files that do not follow your source coding standards. We chose to follow what Google used for their Checkstyle as the basis for ours. We made small tweaks to their standard, but those are contained within the declared file. Please note that Google used a new rule that wasn’t contained within the libraries that were included by the version of the maven-checkstyle-plugin that was available. To enable this additional functionality, we explicitly declared the version of the puppycrawl checkstyle dependency to use.

I configured the plugin to generate reports and to not fail on error, but to give warnings instead.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>2.17</version>
    <dependencies>
        <!--
        http://mvnrepository.com/artifact/com.puppycrawl.tools/checkstyle
        Needed because Google uses a new checkstyle that isn't included by the regular plugin.
        https://groups.google.com/forum/#!topic/checkstyle/OXux8RqJuXg
        -->
        <dependency>
            <groupId>com.puppycrawl.tools</groupId>
            <artifactId>checkstyle</artifactId>
            <version>6.19</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <goals>
                <goal>checkstyle</goal>
            </goals>
            <phase>verify</phase>
        </execution>
    </executions>
    <configuration>
        <configLocation>twinspires_checkstyle.xml</configLocation>
        <encoding>UTF-8</encoding>
        <consoleOutput>true</consoleOutput>
        <failOnViolation>false</failOnViolation>
        <linkXRef>false</linkXRef>
        <enableRulesSummary>true</enableRulesSummary>
        <enableSeveritySummary>true</enableSeveritySummary>
        <enableFilesSummary>true</enableFilesSummary>
        <includeTestSourceDirectory>true</includeTestSourceDirectory>
    </configuration>
</plugin>
<plugin>

The output of the plugin will contain the name of the Java class, the line number and a brief description of what the warning is, along with the checkstyle caregory. Example output is below:

[INFO] --- maven-checkstyle-plugin:2.17:checkstyle (default) @ newcam-server ---
[INFO] Starting audit...
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:10: Using the '.*' form of import should be avoided - com.twinspires.cam.entity.*. [AvoidStarImport]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:58: Using the '.*' form of import should be avoided - com.twinspires.cam.response.*. [AvoidStarImport]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:82: First sentence of Javadoc is incomplete (period is missing) or not present. [SummaryJavadoc]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:98: Abbreviation in name 'registerWageringCustomerInRAFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:125: Abbreviation in name 'updateCustomerSSNFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:177: First sentence of Javadoc is incomplete (period is missing) or not present. [SummaryJavadoc]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:194: First sentence of Javadoc is incomplete (period is missing) or not present. [SummaryJavadoc]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:350:5: Missing a Javadoc comment. [JavadocMethod]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:363:5: Missing a Javadoc comment. [JavadocMethod]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:517:9: '}' at column 9 should be on the same line as the next part of a multi-block statement (one that directly contains multiple blocks: if/else-if/else or try/catch/finally). [RightCurly]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:533:20: Method name 'eCommerceCustomerPreUpdateCheck' must match pattern '^[a-z][a-z0-9][a-zA-Z0-9_]*$'. [MethodName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:538: Abbreviation in name 'emailDTO' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:554: Abbreviation in name 'dtoCountryByISOCode' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:755:5: Missing a Javadoc comment. [JavadocMethod]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:801: Distance between variable 'failReason' declaration and its first usage is 7, but allowed 3.  Consider to make that variable as final if you still need to store its value in advance (before method calls that might do side effect on original value). [VariableDeclarationUsageDistance]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:940: Abbreviation in name 'setRegisterWageringCustomerInRAFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:941: Abbreviation in name 'registerWageringCustomerInRAFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:1155: Abbreviation in name 'setUpdateCustomerSSNFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:1155: Abbreviation in name 'updateCustomerSSNFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:1257: Line is longer than 120 characters (found 129). [LineLength]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/ICustomerServicesManager.java:47: Using the '.*' form of import should be avoided - com.twinspires.cam.response.*. [AvoidStarImport]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/ICustomerServicesManager.java:52: Abbreviation in name 'verifySSN' must contain no more than '1' capital letters. [AbbreviationAsWordInName]

October 19th, 2016

Posted In: java ninja, Javaninja, Maven

Leave a Comment

The Maven Dependency Plugin can give you dependency related information for your Maven projects. I use it for two main purposes

  • Dependencies that I declare, but do NOT use
  • Dependencies that I do NOT care, but do use

The minimal configuration is as follows:

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>analyze</id>
                        <goals>
                            <goal>analyze-only</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

The normal behavior is to break the build when there is a problem. I was working on a pre-existing project and we were going to have to work through the problems over time, so this was counter-productive. To disable this behavior, I set the plugin to to fail on warnings. Another thing that I do is enable the output of XML for any used, but undeclared dependencies. This makes it very easy to copy/paste the missing dependencies.

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>analyze</id>
                        <goals>
                            <goal>analyze-only</goal>
                        </goals>
                        <configuration>
                            <failOnWarning>false</failOnWarning>
                            <outputXML>true</outputXML>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

This is what sample output will look like. There are three different sections.

  • Used, but undeclared dependencies. These should be added to your POM file. You can copy the XML elements in the last section to easily add these.
  • Unused, but declared dependencies. These can usually be removed from your POM files. Please be aware that some dependencies are not explicitly declared and are determined at run-time (e.g Spring has multiple implementations for various functions).
  • The XML for the used, but undeclared dependencies. These XML snippets should be added to your POM files. This prevents your project from breaking when a dependency library changes its dependencies and inadvertantly breaks your build.
[INFO] --- maven-dependency-plugin:2.10:analyze-only (analyze) @ newcam-server ---
[WARNING] Used undeclared dependencies found:
[WARNING]    com.google.code.gson:gson:jar:2.5:compile
[WARNING] Unused declared dependencies found:
[WARNING]    com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:2.7.0:compile
[WARNING]    org.hibernate:hibernate-validator:jar:4.1.0.Final:compile
[WARNING]    mysql:mysql-connector-java:jar:5.1.34:provided
[WARNING]    org.hibernate:hibernate-entitymanager:jar:4.3.11.Final:compile
[WARNING]    org.slf4j:jcl-over-slf4j:jar:1.7.13:compile
[WARNING]    org.springframework:spring-orm:jar:4.2.4.RELEASE:compile
[WARNING]    org.slf4j:log4j-over-slf4j:jar:1.7.13:compile
[WARNING]    org.apache.commons:commons-dbcp2:jar:2.1:test
[WARNING]    org.apache.cxf:cxf-rt-frontend-jaxrs:jar:3.1.4:compile
[WARNING]    ch.qos.logback:logback-classic:jar:1.1.3:compile
[WARNING]    org.codehaus.jackson:jackson-xc:jar:1.9.13:compile
[INFO] Add the following to your pom to correct the missing dependencies: 
[INFO] 
<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.5</version>
</dependency>
[INFO] 

October 19th, 2016

Posted In: java ninja, Javaninja, Maven

Leave a Comment

We had a need to include information about the current Git version of the files in the build for the purposes of troubleshooting the deploy process.

I stumbled upon the maven Git Commit Id plugin that provides that functionality.

Here is a list of the values that it makes available.

git.build.user.email
git.build.host
git.dirty
git.remote.origin.url
git.closest.tag.name
git.commit.id.describe-short
git.commit.user.email
git.commit.time
git.commit.message.full
git.build.version
git.commit.message.short
git.commit.id.abbrev
git.branch
git.build.user.name
git.closest.tag.commit.count
git.commit.id.describe
git.commit.id
git.tags
git.build.time
git.commit.user.name

These values are made available to replace elements in your specified directory that have the proper key and are surrounded with ${}. An example is.

<constructor-arg index="0" value="${git.commit.id.abbrev}"/>

Here are the steps to using it.

Step 1 – Set the resources directory as modifiable

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

Step 2 – Include the Git Commit Id plugin

<plugins>
    <plugin>
        <groupId>pl.project13.maven</groupId>
        <artifactId>git-commit-id-plugin</artifactId>
        <version>2.2.0</version>
        <executions>
            <execution>
                <goals>
                    <goal>revision</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <!--
                If you'd like to tell the plugin where your .git directory is,
                use this setting, otherwise we'll perform a search trying to
                figure out the right directory. It's better to add it explicitly IMHO.
            -->
            <dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
        </configuration>
    </plugin>
</plugins>

Additional configuration

To have the plugin generate a file with all of it’s values, instead of replacing values in your source, use the generateGitPropertiesFile configuration. The file will be created in whatever directory you specified in the resources element in Step 1.

<configuration>
    <!-- this is false by default, forces the plugin to generate the git.properties file -->
    <generateGitPropertiesFile>true</generateGitPropertiesFile>
</configuration>

An example of a generated git.properties is below.

#Generated by Git-Commit-Id-Plugin
#Thu Jan 14 13:32:07 EST 2016
git.build.user.email=norris.shelton@java.ninja.com
git.build.host=cdimac0001.java.ninja.com
git.dirty=true
git.remote.origin.url=https\://github.java.ninja.com/my_project.git
git.closest.tag.name=
git.commit.id.describe-short=4b3fea6-dirty
git.commit.user.email=norris.shelton@java.ninja.com
git.commit.time=14.01.2016 @ 11\:19\:21 EST
git.commit.message.full=r18.i1 - US12193 - Fixing package scan in Spring Config files
git.build.version=3.17.0
git.commit.message.short=r18.i1 - US12193 - Fixing package scan in Spring Config files
git.commit.id.abbrev=4b3fea6
git.branch=R18tc
git.build.user.name=Norris Shelton
git.closest.tag.commit.count=
git.commit.id.describe=4b3fea6-dirty
git.commit.id=4b3fea6863da76d37980527b0cc28310f88d7540
git.tags=
git.build.time=14.01.2016 @ 13\:32\:07 EST
git.commit.user.name=Norris.Shelton

By default, the plugin will search for your .git directory. The plugin documentation recommends explicitly setting it. If you are using a multi-module project, you will need to point your dotGitDirectory to a directory above what it expects.

<configuration>
    <!--
        If you'd like to tell the plugin where your .git directory is,
        use this setting, otherwise we'll perform a search trying to
        figure out the right directory. It's better to add it explicitly IMHO.
    -->
    <dotGitDirectory>${project.basedir}/../.git</dotGitDirectory>
</configuration>

To instruct the plugin to work in verbose mode, specify the following configuration.

<configuration>
    <!-- false is default here, it prints some more information during the build -->
    <verbose>true</verbose>
</configuration>

January 15th, 2016

Posted In: Git, GitHub, Java, java ninja, Javaninja, Maven

Leave a Comment

As a follow-up to Cucumber Springframework Integration, I needed to get more flexibility to control when Cucumber was ran. With the current configuration, the Cucumber tests ran every time the Maven build ran. This is as designed, but we needed more flexibility. We also needed the ability to run the Cucumber tests, but without building the code again. I wanted to build the code once, then tests it as it was deployed to each environment. I wanted to ensure that the golden build was the only build that I was using. This cuts down on the variables when there are problem. If this code works in DEV, but doesn’t work in the next environment, then it is something different in the environments.

Controlling database connections per environment

We had the problem where we thought that the Cucumber tests were being ran against our integration environment. This didn’t turn out to be true, because the Cucumber tests were using the same datasource as our Junit tests. Those always ran against our development environment. To overcome the datasource issue, I moved from a single datasource to a datasource per environment and using Spring profiles to determine which database connection to use. As example of what I placed in the Springframework configuration is:

    <beans profile="default">
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://dev_dns/my_database?zeroDateTimeBehavior=convertToNull"
              p:username="_username_"
              p:password="_password_"
              p:maxWait="3000"
              p:maxIdle="100"
              p:maxActive="10"/>
    </beans>
    <beans profile="dev">
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://dev_dns/my_database?zeroDateTimeBehavior=convertToNull"
              p:username="_username_"
              p:password="_password_"
              p:maxWait="3000"
              p:maxIdle="100"
              p:maxActive="10"/>
    </beans>
    <beans profile="ite">
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://ite_dns/my_database?zeroDateTimeBehavior=convertToNull"
              p:username="_username_"
              p:password="_password_"
              p:maxWait="3000"
              p:maxIdle="100"
              p:maxActive="10"/>
    </beans>

Activating a Spring profile

Now that you have multiple Spring profiles, you can control which profile is active by passing the following to the JVM running the program. If you don’t specify a profile, then default is assumed.

-Dspring.profiles.active=dev

Moving the Cucumber to it’s own Maven module

Once I had that, I realized that having the Cucumber tests as part of the code module caused a problem. Maven processes the build lifecycle steps in sequence. If I wanted to run the tests, then the code would have to be built. This was exactly what I did not want. To overcome this, I needed to move the Cucumber to it’s own module, then if I ran the tests via Maven, there wouldn’t be any code to compile, because the code would be in another module.

I started with a module named bonus-core. This had all of the business logic and the Junit and Cucumber that was used to tests the business logic. I moved all of the Cucumber specific dependencies, steps and features to a new module named bonus-core-integration. This is what the new module’s pom.xml contained:

<?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>
    <parent>
        <artifactId>bonus</artifactId>
        <groupId>com.cdi.igs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>bonus-core-integration</artifactId>

    <dependencies>
        <!-- The module containing the code to be tested with Cucumber -->
        <dependency>
            <groupId>com.cdi.igs</groupId>
            <artifactId>bonus-core</artifactId>
            <version>${project.parent.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--Cucumber related-->
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-jvm-deps</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-spring</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Once this was done, I could run the bonus-code module and execute the Junit tests, but without the Cucumber tests. Since the Cucumber was not in bonus-code-integration, I could now run just the Cucumber tests, but without building.

Parent pom.xml with and without integration tests

This was closer to what I wanted, but it means that If I run mvn test from the parent pom.xml, the Cucumber tests were always ran. I wanted the flexibility to run the main build without the Cucumber tests. I wanted this because we had a previous project that had so many Cucumber tests, that they took 20 minutes to run. We didn’t have this problem in this project, but I didn’t want to have to scramble if that situation developed. I started with the following modules in the parent pom.xml:

<modules>
    <module>bonus-models</module>
    <module>bonus-core</module>
    <module>bonus-services</module>
    <module>bonus-core-integration</module>
</modules>

I wanted the ability to turn off the execution of the integration modules when needed. I came up with the following:

    <profiles>
        <profile>
            <id>bonus-all</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <modules>
                <module>bonus-models</module>
                <module>bonus-core</module>
                <module>bonus-services</module>
                <module>bonus-core-integration</module>
            </modules>
        </profile>
        <profile>
            <id>bonus-no-integration</id>
            <modules>
                <module>bonus-models</module>
                <module>bonus-core</module>
                <module>bonus-services</module>
            </modules>
        </profile>
    </profiles>

This means that if the developer doesn’t do anything, then they will get all of the modules. They can specify the alternative profile to exclude to the integration tests for this project. I named them _project_-all and _project_-no-integration because we also had the case there there was a master pom for this project. If the project was opened from that view, I wanted the ability to turn off the integration for individual projects, not just all of them across all of the projects under the master pom.xml.

The end result

Once all of my changes were completed, I was able to control all of the test execution according to whatever situation I was in at the time.

  • All tests – I could run the Junit tests and the integration tests simply by running mvn clean test from the parent pom.xml
  • Only Junit tests – I could run the Junit tests and skip all the integration tests by running mvn clean test -Pbonus-no-integration from the parent pom.xml
  • Only Integration tests – I could run the integration tests and skip the build and the Junit tests by running mvn clean test from within the bonus-core-integration module instead of the parent module. If this is used, then the code would be retrieved from our local Artifactory.

Creating a SOAP client for a SOAP web service is very straight forward when using CXF. Given a WSDL url or a WSDL file, create a project with a pom.xml containing the following:

<?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>com.rightnow.ws</groupId>
    <artifactId>rightnow</artifactId>
    <version>1.0-SNAPSHOT</version>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.0.4</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>https://twinspires--tst.custhelp.com/cgi-bin/twinspires.cfg/services/soap?wsdl</wsdl>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

When you run mvn compile, it will generate classes in the generated-sources/cxf directory.

The next step is to use the service client. The normal usage pattern is below. The service is instantiated, then the port is retrieved.

package com.rightnow.ws.wsdl.v1_2;

import com.rightnow.ws.metadata.v1_2.MetaDataClass;
import org.junit.Test;

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

import static org.junit.Assert.assertNotNull;

/**
 * @author norris.shelton
 */
public class TestRightNowSyncService {

    @Test
    public void testName() throws Exception {
        RightNowSyncService rightNowSyncService = new RightNowSyncService();
        RightNowSyncPort rightNowSyncPort = rightNowSyncService.getRightNowSyncPort();

        XMLGregorianCalendar metaDataLastChangeTime = rightNowSyncPort.getMetaDataLastChangeTime();
        assertNotNull(metaDataLastChangeTime);

        List<MetaDataClass> metaDataClasses = rightNowSyncPort.getMetaData();
        assertNotNull(metaDataClasses);

    }
}

April 1st, 2015

Posted In: CXF, Java, java ninja, Javaninja, Maven, SOAP

Tags: , , , , ,

2 Comments

This is something that I rarely do because I use IntelliJ IDEA. Here is one of the most complex commands I issue for Maven.

mvn -P dev -DskipTests=true clean install
  • -p dev -> which profile to run
  • -DskipTests=true -> do not run the automated tests
  • clean install -> clean all target directories and run an install

February 13th, 2015

Posted In: Maven

Tags: , , , ,

One Comment

Every once in a while, you have a library that isn’t in a repository and shouldn’t be there for some reason. We had such a library. How do you use that library when it is on your local system instead of in a repository? Use the system scope.

<dependency>
      <groupId>com.this.and.that</groupId>
      <artifactId>non-repo-library</artifactId>
      <version>1.0</version>
      <scope>system</scope>
      <systemPath>/my/path/to/lib.jar</systemPath>
    </dependency>

System scope tells Maven that the dependency is on the file system. System Path tells Maven where to find the dependency.

December 10th, 2014

Posted In: Maven

Tags: , , , ,

Leave a Comment

We were given a project that built a custom server. They had the need to deploy the code and the dependencies. The dependencies were placed in /lib via the maven-dependency-plugin. They were generating the MANIFEST.MF via the maven-jar-plugin. The problem was that what was in the manifest wasn’t exactly what was in the lib directory. An example is in the lib directory, there was a file named adapter-models-1.0-SNAPSHOT.jar. Unfortunately, the manifest contained adapter-models-1.0-20141201.182800-510.jar in the Class-Path, which was the unique snapshot identifier.

I fixed this by adding useUniqueVersions to their manifest element of the maven-jar-plugin.

            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.this.and.that</mainClass>
                            <useUniqueVersions>false</useUniqueVersions>
                        </manifest>
                        <manifestEntries>
                            <Implementation-Version>${project.version}-${changeSet:} (${changeSetDate:})
                            </Implementation-Version>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

December 1st, 2014

Posted In: Maven

Tags: , , , ,

Leave a Comment

The benefit of having a local Maven repository is that only one developer suffers the delay of the network traffic requests to repo1. If you have a local maven repository and your developers have mirrors setup in their settings.xml

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <mirrors>
        <mirror>
            <id>artifactory</id>
            <mirrorOf>*</mirrorOf>
            <url>http://artifactory.cdinteractive.com:8080/artifactory/repo</url>
            <name>Artifactory</name>
        </mirror>
    </mirrors>
</settings>

March 3rd, 2014

Posted In: Maven

Tags: , , , , ,

One Comment

Next Page »
LinkedIn Auto Publish Powered By : XYZScripts.com