Using Dependency Injection in a Java SE Application

It would be nice to decouple components in client applications the way that we have become accustom to doing in server side applications and providing a way to use mock implementations for unit testing.

Fortunately it is fairly straightforward to configure a Java SE client application to use a dependency injection framework such as Weld.

The first step is to include the weld-se jar as a dependency in your project. The weld-se jar is basically the Weld framework repackaged along with its other dependencies as a single jar file which is about 4MB.

    <dependency>
        <groupId>org.jboss.weld.se</groupId>
        <artifactId>weld-se</artifactId>
        <version>2.2.11.Final</version>
    </dependency>

 

Implement a singleton which will create and initialize the Weld container and provide a method to access a bean from the container.

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;


public class CdiContext {

    public static final CdiContext INSTANCE = new CdiContext();

    private final Weld weld;
    private final WeldContainer container;

    private CdiContext() {
        this.weld = new Weld();
        this.container = weld.initialize();
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                weld.shutdown();
            }
        });
    }

    public <T> T getBean(Class<T> type) {
        return container.instance().select(type).get();
    }
}

 

Once you have the context you can then use it to instantiate a bean which in turn will inject any dependencies into the bean.

import java.util.HashMap;
import java.util.Map;


public class MainClass {
    protected String baseDir;
    protected String wldFileLocation;
    protected String dataFileDir;
    protected int timeInterval = 15;
    protected String outputFileDir;

    public void run() throws Exception {
        CdiContext context = CdiContext.INSTANCE;

        //Get an isntance of the bean from the context
        IMatcher matcher = context.getBean(IMatcher.class);

        matcher.setCommodityTradeTimeMap( getDateTranslations(1, "6:30:00 AM", "6:35:00 AM", "6:45:00 AM") );

        matcher.matchTrades(wldFileLocation, dataFileDir, timeInterval, outputFileDir);

    }

What is great is that there are no annotations required on the interfaces or their implementing classes. Weld will automatically find the implementation and inject it in the class where defined. ie. there were no annotations required on the IDataFileReader interface or its implementing classes in order to @Inject it into the Matcher class. Likewise neither the IMatcher interface nor the Matcher class require annotations in order to be instantiated by the CdiContext above.

public class Matcher implements IMatcher {

    //Framework will automatically find and inject
    //an implementation of IDataFileReader

    @Inject
    protected IDataFileReader dataFileReader;

twitter: @RobTerpilowski
@LimitUpTrading

Advertisements

GMapsFX Version 1.0.0

GMapsFX 1.0.0 has been released and supports the following features.

  • Markers
  • Marker Animations
  • Info Windows
  • Polylines
  • Shapes
    • Circles
    • Polygons
    • Rectangles
  • Map State Events
    • Bounds Changed
    • Center Changed
    • Drag
    • Drag End
    • Drag Start
    • Heading Changed
    • Idle
    • MapTypeId Changed
    • Projection Changed
    • Resize
    • Tiles Loaded
    • Tilt Changed
    • Zoom Changed
  • Map UI Events
    • Click
    • Double Click
    • Mouse Move
    • Mouse Up
    • Mouse Down
    • Mouse Over
    • Mouse Out
    • Right Click

Below is a sample map displaying some of the new features including markers, a polyline, an info window, a rectangle, and a circle, as well as events which are monitoring the lat/long position of the center of the map as it is dragged around and updates the value at the top of the UI.

 

enter image description here

Special thanks to Geoff Capper who contributed the shape and event handling code for the latest version.

The GMapsFX.jar file can be found here
Project documentation can be found here and with Javadocs located here

Written with StackEdit.

Creating a Deployment Pipeline with Jenkins, Nexus, Ant and Glassfish

In a previous post I discussed how we created a build pipeline using Jenkins to create application binaries and move them into our Nexus repository. (Blog post here)  In this post I will show how we are using Jenkins to pull a versioned binary out of Nexus and deploy to one of our remote test, staging or production Glassfish servers.  By remote I mean that the Glassfish instance does not live on the same box as the Jenkins CI instance, but both machines are on the same network.

In a previous post I also discussed how to set up Glassfish v3 to allow deployments pushed from remote servers (Blog post here), so if you haven’t explicitly configured your Glassfish to allow this feature, you will need to do so before you get started.

On our Jenkins CI box we have an Ant script, which will be executed by a Jenkins job manually kicked off by a user. The script defines tasks to perform the following operations:

  • Ensure all needed parameters were entered by the user. (app name, version number, admin username/password, etc).
  • Copy the specified version of the application from Nexus to a local temp directory for deployment to a remote Glassfish instance.
  • Undeploy a previous version of the app from the target Glassfish instance.  (Optional).
  • Deploy the app from the temp directory to the target Glassfish instance.
  • Record the deployment info in a deployment tracking database table.  Historical deployment info can then be viewed from a web app.

Ant Script

Below are some of the more interesting code snippets from our Ant script that will be doing the heavy lifting for our deployment pipeline.  The first code snippet below defines the Ant tasks needed for deploying and undeploying applications from Glassfish.  These Ant tasks are bundled with Glassfish, but not installed by default.  If you haven’t installed them, you will need to do so from your Glassfish update center admin page.

<taskdef name="sun-appserv-deploy" classname="org.glassfish.ant.tasks.DeployTask">
   <classpath>
      <pathelement location="/nas/apps/cisunwk/glassfish311/glassfish/lib/ant/ant-tasks.jar"/>
   </classpath>
</taskdef>

<taskdef name="sun-appserv-undeploy" classname="org.glassfish.ant.tasks.UndeployTask">
   <classpath>
      <pathelement location="/nas/apps/cisunwk/glassfish311/glassfish/lib/ant/ant-tasks.jar"/>
   </classpath>
</taskdef>

Once we have the tasks defined, we create a new target for pulling the binary from Nexus and copying it to a temporary location from where it will be deployed to Glassfish.

<target name="copy.from.nexus">
   <echo message="copying from nexus"/>    
   <get src="http://cisunwk:8081/nexus/content/repositories/Lynden-Java-Release/com/lynden/${app.name}/${version.number}/${app.name}-${version.number}.${package.type}" dest="/tmp/${app.name}-${version.number}.war"/>
</target>

Next is a target to undeploy a previous version of the application from Glassfish.  This step is optional and only executed if the user specifies a version to undeploy from Jenkins.

<target name="undeploy.from.glassfish" if="env.Undeploy_Version">
   <echo message="Undeploying app: ${app.name}-${undeploy.version}"/>
   <echo file="/tmp/gf.txt" message="AS_ADMIN_PASSWORD=${env.Admin_Password}"/>
   <sun-appserv-undeploy name="${app.name}-${undeploy.version}" host="${server.name}" port="${admin.port}" user="${env.Admin_Username}" passwordfile="/tmp/gf.txt" installDir="/nas/apps/cisunwk/glassfish311"/>
   <delete file="/tmp/gf.txt"/>
</target>

Next, we then define a target to do the deployment of the application to Glassfish.

<target name="deploy.to.glassfish.with.context" if="context.is.set">
    <sun-appserv-deploy file="/tmp/${app.name}-${version.number}.war" name="${app.name}-${version.number}" force="true" host="${server.name}" port="${admin.port}" user="${env.Admin_Username}" passwordfile="/tmp/gf.txt" installDir="/nas/apps/cisunwk/glassfish311" contextroot="${App_Context}"/>
</target>

And then finally, we define a target, which will invoke a server and pass information to it, such as app name, version, who deployed the app, etc.so that it can be recorded in our deployment database.

<target name="tag.uv.deploy.file">
   <tstamp>
      <format property="time" pattern="yyyyMMdd-HHmmss"/>
   </tstamp> 

   <!--Ampersand character for the URL -->
   <property name="A" value="&amp;"/>
   <get src="http://shuyak.lynden.com:8080/DeploymentRecorder/DeploymentRecorderServlet?app=${app.name}${A}date=${time}${A}environment=${deploy.env}${A}serverName=${server.name}${A}serverPort=${server.port}${A}adminPort=${admin.port}${A}serverType=${server.type}${A}version=${version.number}${A}who=${deploy.user}${A}contextName=${App_Context}" dest="/dev/null"/>

   <echo message="tagging deployment info to UV"/>
</target>

Jenkins Configuration

Now that we have an Ant script to perform the actions that we need to do a deployment, we set up a Jenkins job to deploy to servers in each one of our environments (test/staging/prod).
image


In order to kick off a deployment to one of our servers, the appropriate environment is selected from the screenshot above, and the “Build Now” link is clicked which presents the user with the screen below. In this case we are deploying to a test Glassfish domain named “bjorn” on unga.lynden.com
image

The user can select from the drop down list the server they wish to deploy to and the application they wish to deploy.  The version number is a required entry in a text field.  If the script can’t find the specified version in Nexus, the build will fail.  There are also optional parameters for specifying an existing version to undeploy as well as an application context in the event the app name shouldn’t be used as the default context.


In the screenshot below we are deploying version 5.0.0 of the Crossdock App to the “bjorn” domain running on unga.lynden.com

image


Once the job completes, if we log into the bjorn Glassfish admin page on unga.lynden.com, we see that Crossdock-5.0.0 has been deployed to the server.

image


The screenshot below is an example of undeploying version 5.0.0 of Crossdock, and deploying version 5.0.1 of Crossdock.  Also, in this example, we are telling the script that we want the web context in Glassfish to be /Crossdock, rather than the default /Crossdock-5.0.1

image


The screenshot of the Glassfish admin page below shows that Crossdock-5.0.0 has been unistalled, and that Crossdock-5.0.1 is now installed with a Context Root of /Crossdock.

image


Deployment History

Finally, as I mentioned previously, the Ant script is also saving the deployment information to a historical deployment table in our database.  We have written a simple web application which will display this historical data.  The screenshot below shows all of the applications that have been deployed to our test environment. (We have similar pages for Staging and Production as well).  Included in this information is the application name, version number, date, and who deployed it, among some other miscellaneous info.

image

We can then drill into the history of a specific application by clicking the “Crossdock” link on the screen above and get a detailed history about the deployments for that application including version numbers or dates.  We maintain more than 60 different web applications serving various purposes here at Lynden, so this has been a great tool us to see exactly what versions of our applications are currently deployed where, as well as see the history of the deployment of a specific application in the event we need to roll back to a previous version.

image

As we have learned firsthand, Jenkins is a very useful and versatile tool that is easy to extend for purposes beyond automated builds and continuous integration.

twitter: @RobTerp

Stamping Version Number and Build Time in a Properties File with Maven

Stamping the version number and the build time of an application in a properties file so that it could be displayed by an application at runtime seemed like it should be a pretty straightforward task, although it took a bit of time to find a solution that didn’t require the timestamp, version, or ant-run plugins.

I started with a version.txt file at the default package level in src/main/resources of my project, which looks as follows.

version=${pom.version}
build.date=${timestamp}

By default the Maven resources plug-in will not do text substitution (filtering), so it will need to be enabled within the <build> section of the pom.xml file.

<resources>
   <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
   </resource>
</resources>

Maven does actually define a ${maven.build.timestamp} property which in theory could have been used in the version.txt file, rather than the ${timestamp} property, but unfortunately a bug within Maven prevents the ${maven.build.timestamp} property from getting passed to the resource filtering mechanism.  (issue: http://jira.codehaus.org/browse/MRESOURCES-99).

The workaround is to create another property within the pom.xml file and set that new property to the timestamp value, in this case, the property name is “timestamp”, which is used above in the version.txt file.  The maven.build.timestamp.format is an optional property for (obviously) defining the timestamp format.

<properties>
   <timestamp>${maven.build.timestamp}</timestamp>

   <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
</properties>

Now, when the build is executed we end up with the version number and build time of the application in the version.txt file.

version=1.0.2-SNAPSHOT
build.date=2012-03-16 15:42

twitter: @RobTerp
twitter: @LimitUpTrading

Creating a build pipeline using Maven, Jenkins, Subversion and Nexus.

For a while now, we had been operating in the wild west when it comes to building our applications and deploying to production.  Builds were typically done straight from the developer’s IDE and manually deployed to one of our app servers.  We had a manual process in place, where the developer would do the following steps.

  • Check all project code into Subversion and tag
  • Build the application.
  • Archive the application binary to a network drive
  • Deploy to production
  • Update our deployment wiki with the date and version number of the app that was just deployed.

The problem is that there were occasionally times where one of these steps were missed, and it always seemed to be at a time when we needed to either rollback to the previous version, or branch from the tag to do a bugfix.  Sometimes the previous version had not been archived to the network, or the developer forgot to tag SVN.  We were already using Jenkins to perform automated builds, so we wanted to look at extending it further to perform release builds.

The Maven Release plug-in provides a good starting point for creating an automated release process.  We have also just started using the Nexus Maven repository and wanted to incorporate that as well to archive our binaries to, rather than archiving them to a network drive.

The first step is to set up the project’s pom file with the Deploy plugin as well as include configuration information about our Nexus and Subversion repositories.

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-release-plugin</artifactId>
   <version>2.2.2</version>
   <configuration>
      <tagBase>http://mks:8080/svn/jrepo/tags/Frameworks/SiestaFramework</tagBase>
    </configuration
</plugin>

The Release plugin configuration is pretty straightforward. The configuration takes the subversion URL of the location where the tags will reside for this project.

The next step is to configure the SVN location where the code will be checked out from.

<scm>
   <connection>scm:svn:http://mks:8080/svn/jrepo/trunk/Frameworks/SiestaFramework</connection>
   <url>http://mks:8080/svn</url>
</scm>

The last step in configuring the project is to set up the location where the binaries will be archived to.  In our case, the Nexus repository.

<distributionManagement>
   <repository>
       <id>Lynden-Java-Release</id>
       <name>Lynden release repository</name>
       <url>http://cisunwk:8081/nexus/content/repositories/Lynden-Java-Release</url>
    </repository>
</distributionManagement>

The project is now ready to use the Maven release plug-in.  The Release plugin provides a number of useful goals.

  • release:clean – Cleans the workspace in the event the last release process was not successful.
  • release: prepare – Performs a number of operations
    • Checks to make sure that there are no uncommitted changes.
    • Ensures that there are no SNAPSHOT dependencies in the POM file,
    • Changes the version of the application and removes SNAPSHOT from the version.  ie 1.0.3-SNAPSHOT becomes 1.0.3
    • Run project tests against modified POMs
    • Commit the modified POM
    • Tag the code in Subversion
    • Increment the version number and append SNAPSHOT.  ie 1.0.3 becomes 1.0.4-SNAPSHOT
    • Commit modified POM
  • release: perform – Performs the release process
    • Checks out the code using the previously defined tag
    • Runs the deploy Maven goal to move the resulting binary to the repository.

Putting it all together

The last step in this process is to configure Jenkins to allow release builds on-demand, meaning we want the user to have to explicitly kick off a release build for this process to take place.  We have download and installed the Release Jenkins plug-in in order to allow developers to kick off release builds from Jenkins.  The Release plug-in will execute tasks after the normal build has finished.  Below is a screenshot of the configuration of one of our projects.  The release build option for the project is enabled by selecting the “Configure release build” option in the “Build Environment” section.

The Maven release plug-in is activated by adding the goals to the “After successful release build” section.  (The –B option enables batch mode so that the Release plug-in will not ask the user for input, but use defaults instead.)

image

Once the release option has been configured for a project there will be a “Release” icon on the left navigation menu for the project. Selecting this will kick off a build and then the Maven release process, assuming the build succeeds.

image

Finally a look at SVN and Nexus verifies that the build for version 1.0.4 of the Siesta-Framework project has been tagged in SVN and uploaded to Nexus.

image

image

The next steps for this project will be to generate release notes for release builds, and also to automate a deployment pipeline, so that developers can deploy to our test, staging and production servers via Jenkins rather than manually from their development workstations.

twitter: @RobTerp
blog: https://rterp.wordpress.com

Issues with Server Names and Maven Subversion scm:tag goal

So I was attempting to set up the Maven SCM plug-in to tag our release builds, but encountered a nasty error when executing the scm:tag goal.  The set up in the project’s pom.xml file is below:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-scm-plugin</artifactId>
    <version>1.0</version>
    <configuration>
        <tagBase>http://svn:8080/svn/jrepo/tags/Frameworks/${project.artifactId}</tagBase>
        <tag>${project.version}</tag>
    </configuration>
</plugin>

The error that was encountered when executing the goal was:

C:\Users\robt\Java-Source\Maven-Conversion\SiestaFramework>mvn scm:tag
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building SiestaFramework 1.0.4
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-scm-plugin:1.0:tag (default-cli) @ SiestaFramework ---
[INFO] Final Tag Name: '1.0.4'
[INFO] Executing: svn --non-interactive copy --file C:\Users\robt\AppData\Local\
Temp\maven-scm-1167960694.commit . http://svn:8080/svn/jrepo/tags/Frameworks/SiestaFramework/1.0.4
[INFO] Working directory: C:\Users\robt\Java-Source\Maven-Conversion\SiestaFramework
[ERROR] Provider message:
[ERROR] The svn tag command failed.
[ERROR] Command output:
[ERROR] svn: E235000: In file 'C:\csvn\requirements\subversion\subversion\libsvn _client\commit_util.c' line 474: assertion failed ((copy_mode_root && copy_mode)
 || ! copy_mode_root)

After some research I found some information regarding a potential issue with the SVN client if the server name used in the svn tag command is different than the server name used when the project was checked out.  This was a possibility in my case since our SVN server can be referenced as both svn.lynden.com and mks.lynden.com.

Running the svn info command on the local project directory revealed the following information

C:\Users\robt\Java-Source\Maven-Conversion\SiestaFramework>svn info
Path: .
Working Copy Root Path: C:\Users\robt\Java-Source\Maven-Conversion\SiestaFramework
URL: http://mks:8080/svn/jrepo/trunk/Frameworks/SiestaFramework
Repository Root: http://mks:8080/svn/jrepo
Repository UUID: 14f29ef1-cf71-606c-dc27-f3467c99571f
Revision: 4520
Node Kind: directory
Schedule: normal
Last Changed Author: robt
Last Changed Rev: 4520
Last Changed Date: 2012-02-02 18:07:34 -0800 (Thu, 02 Feb 2012)

As suspected this proved to be the issue.  The project’s pom.xml file defined the server as “svn”, while the project directory was checked out from “mks”.  Changing the tagBase to “mks” cleared the issue and the project could be tagged in SVN using the Maven plugin.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-scm-plugin</artifactId>
    <version>1.0</version>
    <configuration>
        <tagBase>http://svn:8080/svn/jrepo/tags/Frameworks/${project.artifactId}</tagBase>
        <tag>${project.version}</tag>
    </configuration>
</plugin>