hFonti hFonti - 4 months ago 23
Java Question

Parametrize webapp using Jenkins, Maven and JVM parameters

Two objectives:


  1. I want to use parametrized jenkins builds to build a war file and set some properties during the build in the war file.

  2. I also want to be able to refine those properties on the server, where the war file is deployed.



Number 1 is to set properties that fit to the target environment, Number 2 is to be able to quickly change them without having to rebuild the whole application again.

Choosing a maven profile is not flexible enough in this case.

An example would be a port number, that is different for every build, but can spontaneously be changed by the system administrator of the system where the file is deployed.

My idea was to use the maven-resource-filtering plugin to add the build parameters on build into property files. Then on startup of the webapp on the glassfish/tomcat to also look at the set JVM variables.

Am I thinking in the right direction?

Answer

After some research the solution for me looks as followed, for a given parameter "test":

To objective 1.: Use command line parameters when executing maven build, e.g use:

install -Dtest=OnBuildValue

Then use the maven resources plugin, that replaces strings like test=${test} in property files, through the given parameter on build. Add:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>${maven.resources.version}</version>
    <configuration>
        <encoding>UTF-8</encoding>
    </configuration>
</plugin>
...
<build>
...
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
....
</build>

To objective 2.: To be able to change the parameter "test" in the container, without having to rebuild the war file, add a JVM parameter -DTEST=ContainerValue.

Now we need some logic:

private static String getBuildParameter(String paramName)
        throws IOException {
    Resource resource = new ClassPathResource(
            "/META-INF/spring/parameters.properties");
    Properties props = PropertiesLoaderUtils.loadProperties(resource);
    return props.getProperty(paramName);
}

public static String getParameter(final String paramName,
        final String defaultValue, final String logMessage) {
    String value = System.getProperty(paramName);
    if (value!=null) {
        Logger.getLogger(ParameterManager.class.getName()).log(
                Level.WARNING,
                "Parameter: " + paramName + ": " + value
                        + " found in JVM parameters.");
        return value;
    }
    try {
        value = getBuildParameter(paramName.toLowerCase());
    } catch (IOException e) {
        // catch, log exception...
    }
    if (value!=null) {
        Logger.getLogger(ParameterManager.class.getName()).log(
                Level.WARNING,
                "Parameter: " + paramName + ": " + value
                        + " found in parameters set on build time.");
        return value;
    }

    Logger.getLogger(ParameterManager.class.getName()).log(
            Level.WARNING,
            "Parameter: " + paramName + ": " + defaultValue
                    + " as default parameter. " + logMessage);
    return defaultValue;
}

The methods are static because I use them to initialize constants like this:

public static final String TEST_STRING;

static {
    TEST_STRING = getParameter("TEST", "default value",
            "The default is set, please ensure, that this is intended!");
}

Those constants can then be read from everywhere inside your project. How you call this logic, or if you can implement it differently is up to you. I'm sure there are nicer ways, and I would be glad to hear about your implementation, but this works for me.

Note to 1.: If you use Eclipse you can define the maven parameters under Run->Run configurations-> Goals. BUT if you use the glassfish tools (m2e plugin) to deploy your project to your glassfish, it will not use the run configuration. You then have to create the file .m2/settings.xml looking like this:

<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
                      https://maven.apache.org/xsd/settings-1.0.0.xsd>
    <localRepository/>
    <interactiveMode/>
    <usePluginRegistry/>
    <offline/>
    <pluginGroups/>
    <servers/>
    <mirrors/>
    <proxies/>
    <profiles>
        <profile>
            <id>m2e</id>
            <activation>
                <property>
                    <name>m2e.version</name>
                </property>
            </activation>
            <properties>
                <test>xmlparameter</test>
            </properties>
        </profile>
  </profiles>
  <activeProfiles/>
</settings>
Comments