The simplest way to deploy to production with builds

I must confess I'm new to maven world after years of living in the world of monstrous debuild/ant/makefile chimeric constructions. I just don't have yet that very feeling which helps seasoned developer to choose right decisions, and it's looks like there are plenty different ways in maven.

So, I have a simple project containing of web-app. I want basically following:

  • deploy to development Tomcat (I'm using tomcat:deploy), then do nothing.
  • deploy to production Tomcat, but before deployment update git repo, add tagged commit and upgrade build version.

For distinguishing between development and production I've created profiles, dev and prod. The dev profile is activated by default. When I want to deploy something to production, I just type mvn -P prod tomcat:deploy

I've read about release plugin, as well as about buildnumber plugin. But I'm just not sure I'm going the right way.

So, the question is - what is the most succint, self-contained and "mavenish" way to resolve the task I'm asking about.

Answers


Shabunk, as I was commented out, Maven is driving you on the best way to do what you want, inheriting from years of developpers experience.

I would explain what I would (and what I myself really) do.

So you're right to use Maven Release Plugin, plus another one (cargo for instance) You're trying to do two differents things :

  • Identifying a unique version, which means tagging it, and updating pom for a new version
  • Deploying your app (no matter which environnement it is)

Maven Release Plugin

Consider that your own process is maybe ligher than other dev teams are used to. I mean that there is more steps between unit testing and production deployment in larger teams (Q&A, user acceptance, non regression campains). It seems that you make a shortcut linking tag and production deployement. If you want to deploy your webapp on different environnements (integration, user acceptannce, performance, preprod, and so on) you had to identify your version and had to be able to build it again (sure and "repeatable").

That what maven-release-plugin is intended to. It helps you to verify that your source code is clean (all files under source control, with no modification), can compile and pass all tests phases. Then, it deals with pom version and tagging, and finish by storing it for later use in you Maven enterprise repository.

You have many configurations to set up (ditributionManagement, SCM, maven plugin release configuration). But once it is in place, releasing version stand in one simple command line :

mvn release:prepare release:perform

If you want some examples, I can give you some.

<scm>
    <!-- Base URL repository -->
    <url>scm:svn:http://svn.myorg.corp/svn/repository/</url>
    <!-- Developper URL (from trunk or branche) -->
        <developerConnection>scm:svn:http://svn.myorg.corp/svn/repository/trunk/project1</developerConnection>
  <connection>scm:svn:http://svn.myorg.corp/svn/repository/trunk/project1</connection>


</scm>
<build>
    <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-scm-plugin</artifactId>
                <version>1.6</version>
                <configuration>
            <username>${from.settings.xml.user}</username>
            <password>${from.settings.xml.password}</password>
                </configuration>
    </plugin>

    <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-release-plugin</artifactId>
                <version>2.2.2</version>
                <configuration>
                <tagBase>http://svn.myorg.corp/svn/repository/tags/</tagBase>
            <scmCommentPrefix>[DEV#SCM]</scmCommentPrefix>
        </configuration>
    </plugin>
</plugins>
    [...]
</build>
<distributionManagement>
    <repository>
        <id>enterprise_repo</id>
        <name>Enteprise Repository on Artifactory - Stable versions</name>      <url>http://repo.corp:8080/artifactory/prj-xxx-releases</url>
    </repository>
    <snapshotRepository>
        <id>enterprise_repo</id>
        <name>Enteprise Repository on Artifactory - DEV versions</name>
        <url>http://repo.corp:8080/artifactory/prj-xxx-snapshots</url>
    </snapshotRepository>
    <site>
        <id>corporate_site</id>
        <name>Corporate public site</name>
        <!-- must add wagon plugin that support this protocol -->
        <url>ftp://...</url>

    </site>
</distributionManagement>

Deployement

Once again, you may have multiple environnements on which you want to test your app against. There is many plugins that allow you to send and deploy your war : the general cargo-plugin, or more specific ones tomcat-plugin, glassfish-plugin, ... Plugins are giving you the hability to do what you want. Then, configuration can be performed in many ways.

Full Maven way : The full integrated way with Maven is to use Profile and maybe Filters. Profiles let describe properites and behaviour, as you seem to know. Filters are kind of .properties grouping a set of variable that will be used to replace patterns in you xml configuration file into the war (for instance, db connection). It's not the one I use because I find it less flexible than externalizing files. But no matter

Maven with its ecosystem: The way I prefer is to build my apps with Maven and Jenkins (or Continous Integration tool). This is why I'm agree with Aaron when he says that you had to konw limitations of your tools. Using Jenkins, I run every hours/day my application against Unit Tests, generatio Q&A reports, documentation, ... I have a release build that help me to produce all I want to deliver it (to my test team or my customer), and I provide some information to my jobs to deploy it onto different environnements (using maven profiles or jenkins built-in configuration).

It works really fine for me, and I'm pretty sure this is the right way to do.

[EDIT]


Deployment

Once again, deployment mean different phases of Lifecycle.

Local / Dev environnement

I'd never use tomcat:deploy until now, but only because I'd prefer using jetty as light web container (and well integrated with maven). But I'm pretty sure that every kinf of configuration would fit your needs here.

Continuous Integration Environnement In an continous integration environnement, I usually copy the war directly with Jenkins (exporting *.war on the desired machine). The way I do depends on many things :

  • if CI soft is on the same (physical|virtual) server than your app server (Tomcat, Jboss, Weblogic, Glassfish, ...), or distant one => copy time will outstand server refresh time and will produce unsafe deployement (generally corrupted archives)
  • if server is supporting hot reloading (exploded war in web-apps for Tomcat) or at least aware of file system modifications (full war in /deploy for JBoss)
  • if you need to stop your server before, ...

Most of time, it's a simple copy. If I can't, I use some integrated maven plugins (like jahia:deploy plugin for the famous CMS : www.jahia.com), or just cargo plugin : http://cargo.codehaus.org/Maven2+plugin. I don't have any example, but it's really easy to find some on internet because this configuration is often recommanded.

Q&A / Acceptance / Production environnement

For those kind of environnements, which often (as far I as I saw in my job) deals with Service Level Agreement, we (I or admin team) wrote some specific scripts. I'm sure you will be desapointed, but as I mentionned, I don't rely on Maven for everything, and for deployement in particular. IMHO, this is one limit of this tool. You can maybe rely on cargo plugin, or specific ones, but releasing a version, or building one don't match (in time sequence) with real deployement. Even more, I didn't find any plugin that allow me easily to deploy on multiple instances ... and even worth you had to shutdown instances in a specific order (SLA's needs). That said, I didn't mentionned external properties, or SQL scripts, or anything else. They are additional reasons to rely on a dedicated tool.

So, generally, we wrote our own ant / sell scripts. If somebody else have better solutions, I'm obviously interrested in !

I hope I was clear enough.

Regards.


The original question asked for "the best practice" to do production deploys with Maven. I expected to get more than one "best practice" and offered the bounty to see different solutions and learn from them. I do not care if they are pure maven solutions or if they utilize different tools.

Parallel to this thread I did also some researching and would like to post here the different approaches I am seeing. I tried 1) and 3), they are pure maven and regardless what Aaron is saying, they are working also.

In my current project I have a development environment, a test environment, a staging environment and a production environment. From my maven pom I would like to work with the development environment and deploy only to the test environment. I see the following approaches with maven pom:

  1. profiles with filtering properties When working with this approach the deliverable (the war file) has to be rebuilded for different environments. This forced me to offer the bounty because I didn't like that.
  2. profiles wit Maven WAR overlays or assembly plugin This also generated different artefacts for different platforms. But instead of rebuilding the war file it will be unpacked/patched/repacked (for the war overlay) or build and packed with different settings (assembly plugin). I didn't like this solution either.
  3. A deployment plugin (eg. Cargo plugin) without profiles Specify the complete build with installation and also include the cargo plugin doing a remote deployment but not connect it to any phase. So I always work with the development environment but when I explicitely call the cargo plugin it deploys the war from the repository to a container (the test environment). I can even use profiles to distinguish between different containers just for the deployment via cargo.

In all solutions I am using integration test (surefire plugin), version plugin and release plugin for building the war. For the deployment I am implementing approach number 3 following the arguments from https://stackoverflow.com/a/1151870/734687 (a post which expresses very good what I also prefer).

Since the application has to work unchanged in all environment you have to distinguish the different settings on runtime and that can be very complicated. Our operational team wants to have settings per application outside the container so I support an environment var which points me to the proper directory but is optional. If nothing is set then I use the development settings inside the war. See a Spring sample here:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="locations">
        <list>
            <value>classpath:META-INF/spring/*.properties</value>
            <value>file:///${picservice.configdir:-}/*.properties</value>
        </list>
    </property>
</bean>

The properties in the META-INF/spring dir are loaded always. Additionaly I integrate properties from the file system which can override the properties above. If no properties are found this will not trigger an exeception because I set ignoreResourceNotFound. If the property picservice.configdir is set it directs me to an directory, if not than I provided a default value with :. The - says that I want to have no value which is the current directory.

For logging I have a similar solution. You can find very interesting articles about properties file handling with Spring at following links:

I found an interesting presentation about this topic, see Automated Deployment with Maven and friends.


update git repo, add tagged commit and upgrade build version.

-

I've read about release plugin, as well as about buildnumber plugin. But I'm just not sure I'm going the right way.

Yes, using the release plugin is the recommended way. You can use the buildnumber plugin if you want the buildnumber in some document, e.g. CHANGES.txt


I think the simplest way to accomplish what you've specified (if I understood well) is to configure Maven Release Plugin as follows:

<!-- path: project/build/pluginManagement/plugins -->
<plugin>
  <artifactId>maven-release-plugin</artifactId>
  <configuration>
    <releaseProfiles>prod</releaseProfiles>
    <goals>tomcat:deploy</goals> 
  </configuration>
</plugin>

For dev deployments proceed as you're doing: mvn tomcat:deploy -P dev (if dev is active by default remove the -P dev argument).

When releasing proceed in two steps as Maven Release Plugin expects:

mvn release:prepare

This goal will increase the version and tag as you want (and more). Notice that you will also need to specify what SCM Maven should use by configuring this tag in the POM:

<!-- path: project -->
<scm>
  <connection>scm:git:ssh://yourrepo.url</connection>
</scm>

SCM plugin supports a wide variety of SCM managers and protocols (you might be interested in git:http or git:https).

And then

mvn release:perform

This will use the profile prod and simply run the goal tomcat:deploy, exactly what you configured.

I hope this is what you needed. Regards.


The Mavenized solution is not to use Maven when you have an exception to the common case. Maven is all about common cases. Exceptions are handled elsewhere which means the POM (should) never contain any surprises.

Since you need a custom deployment process, write a shell script or an Ant build.xml which does the necessary steps and uses Maven in the process to do the deployment.

That way, each tool does what it can do best.

[EDIT] Note to all the downvoters out there: Unless you have an argument, your down vote means nothing. Maven is a tool. Like all tools, it has limits. The core Maven philosophy is to avoid exceptions. Ant is all about corner cases, Maven is about main stream. From the "What is Maven?" page:

  • Making the build process easy
  • Providing a uniform build system

(my emphasis) So yes, there are things for which Maven isn't the best solution for. If the standard release process works for you, use the release plugin.

But when it doesn't, then look elsewhere instead of falling for the "everything looks like a nail" mind trap.

One of the major personal development steps is to understand your own limits and the limits of the tools that you work with. When a tool isn't suitable for a job, try a different tool instead of trying to warp reality. Your live will be happier.


maybe phing its another tool to solve your problem, its simple, and u can make custom processes

Phing website


Need Your Help

What's the easiest way to deal with project configuration files?

git

It's very common to have at least one configuration file in any project. Every time I share project with git, I have the same problem with:

Draw a simple circle uiimage

ios uiimage core-graphics circle cgcontext

I try to make a 20x20 UIImage with a simple blue circle.