Jenkins File, replacing propeties in file with parameters and sed!

Over the past week I have been trying to update an old web application to run inside a docker container (on Tomcat 8.5.7) which has presented it’s own issues but for the most part has gone pretty smoothly.

After I finally locked down what a basic image looked like, I started to investigate how we could produce environment specific properties files using some templates, Jenkins job parameters and the Linux based sed command.

Mostly this was a pain probably due to my inexperience with sed/Jenkinsfile and just life in general. The main issues I faced were:

  • Replacing with a parameter that contains / (in this case a url string) which was being used as the sed delimiter. This manifests itself as the not so helpful error:
    sed: -e expression #1, char 40: unknown option to 's'

    This was overcome by using a different delimiter, I chose “pipe” (|).

  • The other main issue was the format of how we create the template items to be replaced has some special characters in them, that when trying to use the escape character created issues due to some weirdness in Jenkins.
    e.g: (use of characters with special meaning [] and .)

  • A similar issue was when trying to use the single quote with the sh command in the Jenkins file was having conflicts with the quotations in my properties and sed command. The answer was ultimately to define a custom function to so the replacement for me:
    def updateProperty(property, value, file) { 
       escapedProperty = property.replace('[', '\\[').replace(']', '\\]').replace('.', '\\.')
       sh "sed -i 's|$escapedProperty|$value|g' $file"
  • This can then be used in a stage along with a declared parameter for the Jenkins job which ends up looking like:
          string(name: 'SomeServiceSomeFullPathUrl', defaultValue: '', description: 'The amazing parameter'),    
    stage('Load properties from Jenkins params') {
        dir ('war-update/config') {
           updateProperty(':[SomeService.someFullPathUrl]', params.SomeServiceSomeFullPathUrl ,'')



Glassfish 4.1 MalformedURLException when using Kubernetes and Docker.

Due to constraints in the way the JMX is setup, I was finding my glass fish instance could not be stood up when using kubernetes. The main reason was because the way the HOSTNAME was being generated has a character in it that causes the MalformedURLException.

To fix this, I added the following line to my startup script run by my dockerfile:

echo " $HOSTNAME" | tee -a /etc/hosts

This will cause the hostname to resolve to the localhost and should fix the malformed exception.

Domain mapping with crazy domains and squarespace

Anyone who is in IT knows that as soon as you let anyone know what you do for work, you will open yourself up to any number of questions or request for help for absolutely anything that involves anything that resembles a computer.

Recently a friend of mine contacted me asking for some help on setting up his squarespace, so that the domain that he had paid for would link directly to his squarespace (domain mapping). Although this is not really what I do, I told him I would take a look and see what I could find.

In the end the steps were pretty simple but I know he had tried and failed himself to get everything working, so I thought I would create a simple step by step set of instructions that would hopefully help anyone in the same position.

Setting up your crazy domain!

  1. Select domains from My Account.
  2. Go to DNS settings.
  3. Here you should have at least 3 entries

    1. A Record                    > some.ip.address
    2. A Record           >
    3. CNAME Record    >

In the case of my friend he was missing entry C. To rectify this in the DNS settings there is a menu button that can be selected, and the option to add a record.

Select CNAME Record and press add.

The first field should be www and the second (and final field) should be

We are now setup to talk to our squarespace.

Setting up your squarespace!

  1. Login to your squarespace account
  2. Select settings
  3. Select Domains
  4. Select Connect a third party domain
  5. Enter the url of the domain set up in crazy domains, without the www (for our example that is
  6. Select continue
  7. This should add our domain, but the domains DNS settings are not likely to be working properly yet. To sort this out we need to:
    1. In the domains menu, select our domain from the list of third party domains.
    2. Select the DNS settings
    3. We should see a CNAME entry for failing. That is because we need to set it up correctly in crazy domain.

Setting up squarespace verification in your crazy domain!

  1. Go back to your DNS settings in the crazy domain page
  2. Add a new CNAME record like we did before. The difference is in this case we use the collections of letters and numbers that are listed in squarespace under “Host” goes where we put “www” (the first entry) and the goes into our second entry. This takes some time to register with squarespace that you have made the change, so don’t do what I did and assume it doesn’t work, and start trying to fix something that is not broken. They say it can take up to 24 hours for the verification process to finish.

Testing your Setup

We are able to test our setup by going to a dns checking site:

  1. Test your main route:
    1. Put into the field that says
    2. Select A from the list
    3. Click search.
    4. You should see the IP (some.ip.address) listed with a green tick.
  2. Test your CNAME verify
    1. Put <jumbled.numbers.and.letters> into the field that says
    2. Select CNAME from drop down.
    3. Click search
    4. You should see the verify address

It took some time, but we got it going in the end, and the routing was working after a short period of time!

I may have gone into some detail with this, but I was aware that my friend could not find instructions to do it, so I thought why not make a set of my own.

The pain of ETags, mod_deflate, Apache 2.4 and Tomcat 7

While working on a project recently, I found that when a request was made, the web server was always responding with a 200 (OK). When it was the first load, this is of course the expected result, but for subsequent loads, it was expected that the file would be unchanged, and a response of the 304 (Not Modified) would be returned, thus retrieving the data from the cache.

After some investigation it was found that mod_deflate.c, in response to a bug found in Apache 2.2 (in 2008), would append -gzip to the generated ETag, and continue on it’s merry little way. This was found to break the caching as the ETags that were being sent in both the request and the response, although matching, would not match the ETag comparison that is done by the DefaultServlet (checkIfNoneMatch) in Tomcat.

The resulting behavior was always a response of 200 to any GET call that had been touched by mod_deflate, thus making the use of mod_deflate redundant for any load other than the first.

A suggested solution for the problem would be to have the appended “-gzip” removed from the ETag, this could be done in the runtime configuration using the following:

RequestHeader  edit "If-None-Match" "^(.*)-gzip$" "$1"
Header  edit "ETag" "^(.*[^g][^z][^i][^p])$" "$1-gzip"

It appears that this is similar to the solution in Apache 2.5 mod_deflate.c with the addition of the DeflateAlterETAG directive, allowing you to set the value to NoChange, which will prevent mod_deflate from appending/changing the ETag in the first place.

With the pain of ETags becoming more and more apparent, with this issues around mod_deflate’s alteration of the ETag, and other issues around when the same file is served from multiple Apache servers, it was clear that perhaps a different solution was needed.

The solution is of course, to remove ETags all together and rely on other methods for cache busting. It is said in this article that:

It is important to specify one of Expires or Cache-Control max-age, and oneof Last-Modified or ETag, for all cacheable resources. It is redundant to specify both Expires and Cache-Control: max-age, or to specify both Last-Modified and ETag.

As a result we decided to go forward using a combination of Last-Modified, and Cache-Control.

To remove ETags for all situations, make the following changes to the httpd.conf:

<IfModule mod_headers.c>
    Header unset ETag

FileETag None

The second half of the solution is to set the Cache-Control. There are some issues around setting it to private in firefox, so as a result the best solution is to set it as the follows (also in the httpd.conf):

Header set Cache-Control public,max-age=0,no-cache

Note: It is important that there is no space between each of properties.

Both no-cache and max-age=0 are used to force the browser to revalidate with the server before using that cached value, with max-age=0 enforcing the check on the last-modified field.

The setting of public allows both a client cache, and a proxy cache to keep a copy of the files, where as private only allows the client cache. There are some known issues with certain versions of firefox, if you decide to set the cache-control value to private, and are using HTTPS, so it is important to keep that in mind when making the choice between public and private.
The result is the ability to return a 304, in a clustered environment, and while using mod_deflate.c with a combination of Last-Modified and Cache-Control.