Simplifying Build Management – Build Once, Deploy Anywhere

With many types of languages and approaches to development, it becomes apparent that there are also different approaches to build management of artifacts/resources regardless of language. The diagram below is mainly to provide a picture of how deployment artifacts are tightly coupled to the environments in which they run and how to improve build management. This can be done by moving the deployment artifacts towards a more agnostic view of the environment to which they are deployed. Many times what happens is either applications hard code environment specific properties or environmental properties are built into the final deployment artifact.

The following diagram is an explanation of three levels of a “Dependency Maturity Model” that is used to determine how to move the current applications from Level 1 up to Level 3 (which will help provide the strategy to “Build Once, Deploy Anywhere”).

Dependency Maturity Model

Level 1 – Env Embedded

This is the first level of an application and it’s reliance on dependencies such as environment properties. In this case, I want to track where the environment specific properties exist (from the source to the build artifact). The problem with this level is that the applications and components have hard-coded properties such as FTP locations, email addresses, and other properties that are required to change per the environment they exist in. And depending on the development shop you work in, you could have multiple environments (such as Development, Integration, Quality Assurance, User Acceptance Testing, Staging, and Production). When the applications/components are littered with environment specific properties/dependencies, it becomes necessary for build management to rebuild the source into the build/deployment artifact for each environment. So the code not only has to be built for each specific environment, the code has to be modified in order to build it for a specific environment. This will require environment specific coding changes, builds, deployments, and potential testing issues based on the changes that are made for that environment. The build artifact is therefore embedded with environment properties and is only deployable to the environment to which it is built for. This would be the equivalent of having APIs in Java (JARs) that have environment properties littered through the claases and then have those JAR files pulled into a WAR file that has some of the same redundant properties within it. So I need to fix the issue of changing the code for each environment by removing the environmental properties out of the code and put that into an external resource (Level 2).

Level 2 – Env Abstract

This is the second level of an application in which I decide to remove the redundant environment properties from the applications/components and put them into an external resource (such as a property file) that all the applications will import and use. This greatly helps development because know I don’t have the potential for redundant and incorrect properties littering the code (maintaining a single source of information for the properties). This also helps the build management somewhat because the code doesn’t have to be modified before each build is done. This would essentially be like removing the environment properties from the APIs in Java (JARs) and the WAR file and putting those properties in a single file (and JAR) that gets pulled into the final WAR. This definitely was a progression from Level 2, the only problem is that I still have not met the “Build Once, Deploy Anywhere” strategy. The reason is that I have successfully pulled the environment specific properties into a single source of truth (property file/JAR), but when I do a build, I are still building an environment specific artifact. So when I combined the Agnostic Code with the Abstract Environment JAR (for Development), I essentially get a Development only WAR. The same can be said when I take that Agnostic Code with the QA Environment JAR, I will get a QA only war from the build. This requires the deployments to only deploy the environment specific artifact (WAR) to that environment, because it cannot work in any other environment. So the next thing I need to do is remove the environment specific resource from the build artifact (Level 3).

Level 3 – Env Abstract

This is the third level of an application in which I now take the final step to realize “Build Once, Deploy Anywhere”. In order to do this, I need to simply not include the environment specific resource into the final application build artifact. This is really as simple as it sounds, when the application/component is brought together into the final artifact (WAR), the environment specific resource (JAR) is not included. This creates a final build artifact that is agnostic to the environment it is deployed to and therefore only needs to be built once and can be deployed to any environment without issue. This is a huge advantage because essentially through the Development Architecture, the Continuous Integration tool can build the application and deploy it to the Maven Repository and then the same Continuous Integration tool can pull that artifact from the Maven Repository and deploy it to any of the environments. This helps insure that the artifact (WAR) that the QA is testing again is the same exact artifact (WAR) that is being deployed int Staging.

Now you maybe thinking that this sounds good and makes sense, but what about the environment properties in Level 3, where did they go. This is where I can utilize capabilities such as OSGi bundles or application server specific capabilities for shared libraries. In WebLogic, this concept can be accomplished through what is called Optional Packages. Essentially what is done is that through the MANIFEST, I define an entry for the environment specific artifact (JAR) that gives an identification and version for that artifact. Then when the agnostic artifact (WAR) is deployed, it also has an entry in it’s MANIFEST for a dependency on the environment specific artifact (JAR) by listing that identification information and version. This way the environment specific artifact (JAR) can be deployed to the container separately from the WAR and at anytime (a new deploy only requires the dependent WARs to be restarted). For local testing, I utilize the capabilities of Maven to define the environment specific artifact as a test so that it can be used for local testing (since it is excluded from the final build artifact). In an additional blog post, I will go deeper into what I had to do to create these Optional Packages and then finally how to create a custom plugin to deploy these artifacts because the maven-weblogic-plugin does not support the deployment of these types of optional jars (ones that are not configured with WebLogic specific config files).

Share and Enjoy