Skip to content
Snippets Groups Projects
Commit 5e049612 authored by Lyle Hanson's avatar Lyle Hanson
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
target
Readme.md 0 → 100644
# Tomcat File Property Source
This is an implementation of `org.apache.tomcat.util.IntrospectionUtils.PropertySource` which
allows Tomcat to resolve property placeholders in configuration files such as `server.xml` at startup,
sourcing properties from a file path indicated by either the system property or environment variable
(in that order of preference) named `TOMCAT_PROPERTIES_FILE`.
## Why?
It's undesirable and fragile to maintain individual copies of Tomcat configurations across
multiple hosts for the same application. It doesn't scale well operationally and adds opportunity for
misconfiguration or subtle differences between Tomcat instances. Externalizing sensitive and environment-specific
configuration properties (such as keystore or database credentials) allows the application to be deployed
identically across multiple hosts while minimizing the amount individual configuration required.
It also makes a customized Tomcat deployment self-contained for easy packaging with [Docker](https://www.docker.com/).
## Usage
The artifact produced by this project is a fat JAR which is simply copied into your Tomcat `lib/` directory.
In order to register as a property source which Tomcat will consult on startup, the following needs to be
included in `conf/catalina.properties`:
org.apache.tomcat.util.digester.PROPERTY_SOURCE=edu.wisc.services.tomcat.FilePropertySource
At Tomcat startup, if a valid file path is found in the environment or system properties under the
`TOMCAT_PROPERTIES_FILE` key, those properties may be used as placeholders in Tomcat config files.
## Example
To build a Docker image containing a customized Tomcat configuration with externalized properties, here's an
outline of the setup you'd do.
In your project's `pom.xml`:
<build>
<plugins>
<plugin>
<!-- Copy the tomcat-file-property-source JAR from Maven into our build directory -->
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-tomcat-property-source</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>edu.wisc.services.tomcat</groupId>
<artifactId>tomcat-file-property-source</artifactId>
<version>${tomcat-file-property-source-version}</version>
<outputDirectory>${project.build.directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<!-- Include the tomcat-file-property-source JAR in our docker build directory
when "mvn docker:build" is run -->
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<imageName>tomcat_endpoint</imageName>
<dockerDirectory>docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>tomcat-file-property-source-${tomcat-file-property-source-version}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
<!-- ... -->
</plugins>
</build>
To build a Docker image with `mvn docker:build`, you'd include a `Dockerfile` containing the following in your `docker/` directory:
FROM tomcat:8.0.32-jre8
# Set up our custom property resolver for credential placeholders in Tomcat config files
ENV TOMCAT_PROPERTIES_FILE=/my/properties/file
RUN echo "org.apache.tomcat.util.digester.PROPERTY_SOURCE=edu.wisc.services.tomcat.FilePropertySource" >> /usr/local/tomcat/conf/catalina.properties
COPY tomcat-file-property-source-1.0.jar /usr/local/tomcat/lib
# Install custom Tomcat configuration with property placeholders
COPY server.xml /usr/local/tomcat/conf/
That server.xml might include placeholders like so:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
executor="http-executor-8443"
URIEncoding="UTF-8"
SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
sslEnabledProtocols="TLSv1,TLSv1.1,TLSv1.2,SSLv2Hello"
maxKeepAliveRequests="300"
keystoreFile="${keystore.file}"
keystorePass="${keystore.password}" />
Those `keystore.*` placeholders will be resolved from your properties file specified in the `TOMCAT_PROPERTIES_FILE` and
exposed to the Docker image via the usual means, such as [VOLUME](https://docs.docker.com/engine/reference/builder/#volume).
Properties:
keystore.file=/path/to/my/keystore.jks
keystore.password=t0ps3cr3t
pom.xml 0 → 100644
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>edu.wisc.services.tomcat</groupId>
<artifactId>tomcat-file-property-source</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Tomcat File Property Source</name>
<description>Implements org.apache.tomcat.util.IntrospectionUtils.PropertySource to provide runtime
Tomcat configuration values from an external properties file</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<distributionManagement>
<repository>
<id>code.doit-public-releases</id>
<name>code.doit Public Releases</name>
<url>https://code.doit.wisc.edu/maven/content/repositories/public-releases</url>
</repository>
<snapshotRepository>
<id>code.doit-public-snapshots</id>
<name>code.doit Public Snapshots</name>
<url>https://code.doit.wisc.edu/maven/content/repositories/public-snapshots</url>
</snapshotRepository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.4.6</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.0.32</version>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.0-groovy-2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<!-- groovy-eclipse-compiler 2.8.0-01 and later require maven-compiler-plugin 3.1 or higher -->
<version>3.1</version>
<configuration>
<compilerId>groovy-eclipse-compiler</compilerId>
<source>1.8</source>
<target>1.8</target>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.9.1-01</version>
</dependency>
<!-- groovy-eclipse-compiler 2.8.0-01 and later require an explicit dependency on groovy-eclipse-batch -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-batch</artifactId>
<version>2.4.3-01</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.9.1-01</version>
<extensions>true</extensions>
</plugin>
<plugin>
<!-- Produce a fat JAR for Tomcat consumption -->
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package edu.wisc.services.tomcat
import org.apache.tomcat.util.IntrospectionUtils
class FilePropertySource implements IntrospectionUtils.PropertySource {
static final String ENV_KEY = 'TOMCAT_PROPERTIES_FILE'
Properties properties = new Properties()
public FilePropertySource() {
def filename = System.getProperty(ENV_KEY) ?: System.getenv(ENV_KEY)
if (filename) {
try {
FileInputStream fis = new FileInputStream(filename)
properties.load(fis)
fis.close()
} catch (e) {
System.err.println "Could not load properties file at '$filename', caught $e"
}
}
}
@Override
String getProperty(String key) {
properties[key]
}
}
package edu.wisc.services.tomcat
import spock.lang.Specification
import static FilePropertySource.ENV_KEY
class FilePropertySourceTest extends Specification {
def "FilePropertySource reads properties from a file specified in the environment"() {
setup:
File propertiesFile = File.createTempFile( "tomcat-file-property-source-test", ".properties")
propertiesFile.deleteOnExit()
System.setProperty(ENV_KEY, propertiesFile.getCanonicalPath())
when:
propertiesFile << "### Properties file header\n" +
"#oldvalue=old\n" +
"password=12345\n"
then:
'12345' == new FilePropertySource().getProperty('password')
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment