Problem description

Let’s say that your multi-tiered application consists of numerous components:

  • webservice server
  • webservice client
  • business component-logic
  • front-end web application

These components communicate with each-other through SOAP webservices and from time to time your customer is reporting an error which you know is caused by non-compatible changes done in the webservice call – which baiscally means that he did not do a full upgrade – one of the components is at least one version behind.

The project is built using maven and each release has its own version so the idea is to put this version into easily-accessible form so that each time this problem rises developers could ask customer for the version of a component and thus make sure that problem rised is not upgrade issue.

Requirements

So lets first specify requirements for this task:

I want to have version of each component easily accessible for users. Version should be applied automatically . After a release it should not be possible to alter version without recompiling the code.

Solution discussion

Version should be easily accessible

The first requirement is a little bit vauge easily accessible mitght differ from person to person but it’s also stated that person accessing it might have little or nothing to do with development or programming at all, that’s why solution with writing version of component into the log file might not be enough.

Probably the easiest solution is just to point user to a certain URL within application server, let him take screenshot of version and treat it as PREREQUISITE of all bug reports.

All application servers have managed beans and in JBoss there’s also a jmx-console where mbeans can be accessed with their properties and in case sombody still prefers command line – there’s a utility script called twiddle that can access mbeans from command-line. In order for this to work there needs to be mbean configured within JBoss application server.

Version should be applied automatically

Since project is built with maven this requirement is achieved easily – we just need to add resource filtering, apply version number within the resource and make it accessible to the mbean.

It should not be possible to alter version without recompiling the code

This requirement makes it a bit more compliated – using the previous one only, it could have been just a text file added to the jar and than loaded using Property class. Right now the only option is to have somehow java class treated as a resource, this means that first there should be resource filtering applied and java class should be placed in src/main/java and than everything would go as normal.

This makes any changes to this class a bit harder since one must know that version class is someplace else than src/main/java but proper comment should do the trick.

Solution description

Requirement analysis presented us with a view of how the solution should look like:

  • It should be a java class
  • This java class should be filtered using maven resource filtering
  • This java class should have version property applied when doing resource filtering
  • This java class should be treated by JBoss application server as mbean
  • mbean should contain a meaningful name so it’s easy to find it in the jmx console

Let’s start with creating a java class for our mbean:


public class MyVersion {

  private String version = "${project.version}";

  public String getVersion() {

    return version;

  }

}

As you can see in the code sample above, version property does have a setter – this is a way to mark it as readonly property in the JMX console.

Now maven needs to know that this class should be treated as regular resource and apply filtering – thus replacing project.version property with current version number.

So first approach would be to change src/man/java into something different but since it’s a strong convention this proved to be hard (if not impossible).

So another approach is to use special directory called src/main/tpl and add it as additional resource directory whose output should be placed into src/main/java.

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.5</version>
        <executions>
          <execution>
            <id>copy-resources</id>
            <!-- here the phase you need -->
            <phase>validate</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <outputDirectory>${basedir}/src/main/java</outputDirectory>
              <resources>
                <resource>
                  <directory>src/main/tpl</directory>
                  <filtering>true</filtering>
                  <includes>
                    <include>**/*.java</include>
                  </includes>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
    ...
  </build>
  ...
</project>

The only thing that’s left is to register our mbean so it’s available through JMX console. On JBoss there’s special extension of EJB3 specification called POJO service. It’s specific annotation that informes JBoss AS to create single instance of your class and have it registered in JNDI under specified name.

So let’s see how our code changes after applying it:

@Service(objectName = "projectName.VersionClass")
@Management(MyVersion.class)
public class MyVersionImpl implements MyVersion {

 private String version = "${project.version}";

 public String getVersion() {

 return version;

 }

}

As you can see in the code above – there are two annotations @Service and @Management. The latter requires a management interface to be specified so let’s create one:


public interface MyVersion {

String getVersion();

}

And that’s it – now we can always tell our client to first provide us with screenshot of a version before handling any bug reports.

NOTE:  In case you were wondering if it’s possible to add service mbean into existing jar in your project be warned that the author of this post has tried that and JBoss AS tried to register the same mbean twice – first when loading the jar alone and second time when loading the jar as a dependency of embedded war which as you can guess resulted in Exception thrown.

UPDATE: See updated version of this solution – using ejb 3.1 only, compilant with any application server here.