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.
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>
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); } }
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; } }
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!
sheltonn July 11th, 2017
Posted In: Maven, MockMVC, Spring, Spring Boot, Spring MVC, Test Driven Development, Unit Tests
Tags: Spring, spring boot, spring boot web
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]
sheltonn October 19th, 2016
Posted In: java ninja, Javaninja, Maven
The Maven Dependency Plugin can give you dependency related information for your Maven projects. I use it for two main purposes
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.
[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]
sheltonn October 19th, 2016
Posted In: java ninja, Javaninja, Maven
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.
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> </resources> </build>
<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>
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>
sheltonn January 15th, 2016
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.
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>
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
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.
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.
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.
sheltonn November 13th, 2015
Posted In: Cucumber, Integration Tests, Java, java ninja, Javaninja, JUnit, Maven, Spring, Unit Tests
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); } }
sheltonn April 1st, 2015
Posted In: CXF, Java, java ninja, Javaninja, Maven, SOAP
Tags: code generation, cxf, oracle, right now, SOAP, soap client
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
sheltonn February 13th, 2015
Posted In: Maven
Tags: maven, maven profiles, mvn, skip tests, skiptests
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.
sheltonn December 10th, 2014
Posted In: Maven
Tags: dependency, maven, scope, system, systempath
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>
sheltonn December 1st, 2014
Posted In: Maven
Tags: manifest, maven, maven maven-jar-plugin snapshot, maven-jar-plugin, snapshot
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>
sheltonn March 3rd, 2014
Posted In: Maven
Tags: artifactory, dependencies, maven, mirror, mirroring, mirrors