In brewing, krausening (KROI-zen-ing) refers to adding a small amount of existing beer to fresh wort to prime the beer for carbonation. In Java, Krausening is a project to populate finished archives for deployment. This approach allows Properties files to be externalized from deployment units, enabling the same deployment unit to be leveraged repeatedly without the need to rebuild or hack the archive.
In order to use Krausening, the following prerequisites must be installed:
- Maven 3.6+
- Java 8+
For Krausening Python, the following must also be installed:
Krausening is very simple. Follow these steps to prime your project:
- Add a Java System Property called KRAUSENING_BASE pointing to the folder with your .properties files
KRAUSENING_BASE=./src/test/resources/base
- In your source code, get a handle to the Krausening singleton, then request the property file you'd like to access:
Krausening krausening = Krausening.getInstance();
Properties properties = krausening.getProperties("example.properties");
- You're done - order your next pint!
Often, some properties need to change as your deployment unit travels between environments. We want to do this without having to copy and paste all the properties, lowering our maintenance burden to just those properties that have changed. To accomplish this, build on the prior example by:
- Add a Java System Property called KRAUSENING_EXTENSIONS pointing to the folder with your extension .properties files
KRAUSENING_EXTENSIONS=./src/test/resources/prod-env
- Create a properties file of the same name as the one in base, only added the properties you want to extend:
# in $KRAUSENING_BASE/example.properties:
propertyA=org.bitbucket.some.reflect.Class
propertyB=https://localhost/
# in $KRAUSENING_EXTENSIONS/example.properties:
propertyB=https://prodUrl/
- When you look for your properties, you'll now get a collapsed version, containing propertyA from the base version, and propertyB from the extensions version:
Krausening krausening = Krausening.getInstance();
Properties properties = krausening.getProperties("example.properties");
assertEquals(properties.get("propertyA"), "org.bitbucket.some.reflect.Class");
assertEquals(properties.get("propertyB"), "https://prodUrl/");
- You're done - try a mystery beer with Krausening's encryption integration to further quench your thirst.
Sometimes different contexts/applications/classloads/wars want to have their own properties even when deployed in the same environments. For example, foo and bar are deployed together with the same krausening base and extensions set, but foo wants to have my.property=X and bar wants to have my.property=Y. In this case you can leverage override extensions to apply different properties per context.
- Add a Java System Property called KRAUSENING_OVERRIDE_EXTENSIONS pointing to the folder with your override extension .properties files
KRAUSENING_OVERRIDE_EXTENSIONS=./src/test/resources/prod-env-overrides
- Create subfolders for the different contexts you need to override extensions
# in $KRAUSENING_OVERRIDE_EXTENSIONS/foo/example.properties:
my.property=X
# in $KRAUSENING_OVERRIDE_EXTENSIONS/bar/example.properties:
my.property=Y
- A) Update the web.xml files for each context to point to a different subfolder within the override extensions location
web.xml
for foo
<listener>
<listener-class>org.bitbucket.krausening.KrauseningWarSpecificBootstrapContextListener</listener-class>
</listener>
<context-param>
<param-name>override.extensions.subfolder</param-name>
<param-value>foo</param-value>
</context-param>
web.xml
for bar (NOTE the difference in the subfolder parameter)
<listener>
<listener-class>org.bitbucket.krausening.KrauseningWarSpecificBootstrapContextListener</listener-class>
</listener>
<context-param>
<param-name>override.extensions.subfolder</param-name>
<param-value>bar</param-value>
</context-param>
Krausening krausening = Krausening.getInstance("foo");
Properties properties = krausening.getProperties("example.properties");
assertEquals(properties.get("my.property"), "X");
- B) Alternatively, you can use an override subfolder when getting the krausening instance.
Krausening krausening = Krausening.getInstance("foo");
Properties properties = krausening.getProperties("example.properties");
assertEquals(properties.get("my.property"), "X");
Frequently, it is useful to store encrypted information within properties files. Krausening optionally leverages Jasypt to allow stored properties to be encrypted at rest while also decrypting property values as they are read without manual interaction.
- Add a Java System Property called KRAUSENING_PASSWORD pointing to your Jasypt master encryption password.
KRAUSENING_PASSWORD=myMasterPassword
- Use Jasypt to encrypt your property information, then add the encrypted value in your properties file via the Jasypt format:
password=ENC(2/O4g9ktLtJLWYyP8RlEhBfhVxuSL0fnBlOVmXI+qRw=)
- When you look for your property, you'll now get the decrypted value:
Krausening krausening = Krausening.getInstance();
krausening.loadProperties();
Properties properties = krausening.getProperties("encrypted.properties");
assertEquals(properties.get("password"), "someStrongPassword");
- You're done - go for the whole sampler with Krausening's Owner integration if you're still thirsty.
Krausening in Five Pints (Leveraging Owner Integration to Access Properties via Interfaces (and more))#
While accessing properties via java.util.Properties
as String
values works, wouldn't it be great if we could get compile-safe, strongly typed references to our Krausening property values that reflect their actual type? By integrating tightly with Owner, Krausening can offer annotation-based type conversion, variable expansion, hot reloading, XML property file support, and all of the other great features that are built into Owner. Assuming that we have the following properties files created:
# in $KRAUSENING_BASE/example.properties:
fibonacci=1, 1, 2, 3, 5, 8
url=https://localhost
serviceSubPath=foo/baz
# in $KRAUSENING_EXTENSIONS/example.properties:
url=https://prodUrl
fullServiceUrl=${url}/${serviceSubPath}/endpoint
pi=3.1415
- Create an interface that describes and maps to the contents of the collapsed version of
example.properties
. Code that relies on these property values will be able to directly use this interface, instead of interacting with ajava.util.Properties
object. The interface must contain a@KrauseningSources
definition, along with any supported Owner annotation:
@KrauseningSources("example.properties")
public interface ExampleConfig extends KrauseningConfig {
@Key("fibonacci")
List<Integer> getFibonacciSeq();
@Key("fullUrl")
URL getFullUrl();
@Key("pi")
double getPi();
@Key("not-defined-in-prop-file")
@DefaultValue("1234")
int getInt();
}
- Access properties via the newly created interface:
ExampleConfig config = KrauseningConfigFactory.create(ExampleConfig.class);
assertEquals(3, config.getFibonacciSeq().get(3));
assertEquals(new URL("https://prodUrl/foo/baz/endpoint"), config.getFullUrl());
assertEquals(3.1415d, config.getPi());
assertEquals(1234, config.getInt());
- Optionally, get a list of all the properties in the newly created interface by calling the configuration fill() method:
ExampleConfig config = KrauseningConfigFactory.create(ExampleConfig.class);
Properties properties = new Properties();
config.fill(properties);
assertTrue(properties.keySet().contains("pi"));
assertEquals("3.1415",properties.getProperty("pi"));
- Check out
KrauseningConfigTest
insrc/test/java
and/or the Owner documentation for additional information on how to best utilize the Krausening-Owner integration.
You're now 5 pints in and ready for how ever many more property files you need without having to worry about stumbling through deployment!
See the krausening-python README for more details.
Want Krausening in your project? The following Maven dependency will add the Java implementation of Krausening to your Maven project from the Maven Central Repository:
<dependency>
<groupId>org.bitbucket.askllc.krausening</groupId>
<artifactId>krausening</artifactId>
<version>11</version>
</dependency>
Krausening uses both the maven-release-plugin
and the nexus-staging-maven-plugin
to facilitate the release and deployment of new Krausening builds. In order to perform a release, you must:
-
Obtain a JIRA account with Sonatype OSSRH and access to the
org.bitbucket.askllc
project group -
Ensure that your Sonatype OSSRH JIRA account credentials are specified in your
settings.xml
:
<settings>
<servers>
<server>
<id>ossrh</id>
<username>ossrh-jira-id</username>
<password>{encrypted-ossrh-jira-pwd}</password>
</server>
</servers>
</settings>
- Krausening Python requires a PyPI account with access to the krausening project and integrates into the
maven-release-plugin
'sdeploy
phase to appropriately publish the package to PyPI. PyPI account credentials should be specified in yoursettings.xml
under the<id>pypi</id>
<server>
entry:
<settings>
<servers>
<server>
<id>pypi</id>
<username>pypi-username</username>
<password>{encrypted-pypi-password}</password>
</server>
</servers>
</settings>
- Install
gpg
and distribute your key pair - see here. OS X users may need to execute:
export GPG_TTY=`tty`;
- Execute
mvn release:clean release:prepare
, answer the prompts for the versions and tags, and performmvn release:perform
Krausening is available under the MIT License.
Krausening would like to thank Counterpointe Solutions for providing continuous integration and static code analysis services for Krausening.