pkl-config-java Library
The pkl-config-java library builds upon pkl-core. It offers a higher-level API specifically designed for consuming application runtime configuration.
Installation
The pkl-config-java library is available from Maven Central. It requires Java 11 or higher.
Gradle
To use the library in a Gradle project, declare the following dependency:
-
Groovy
-
Kotlin
dependencies {
compile "org.pkl-lang:pkl-config-java:0.25.3"
}
repositories {
mavenCentral()
}
dependencies {
compile("org.pkl-lang:pkl-config-java:0.25.3")
}
repositories {
mavenCentral()
}
Unlike pkl-config-java
, pkl-config-java-all
is a fat Jar with renamed third-party packages to avoid version conflicts.
Maven
To use the library in a Maven project, declare the following dependency:
<project>
<dependency>
<groupId>org.pkl-lang</groupId>
<artifactId>pkl-config-java</artifactId>
<version>0.25.3</version>
</dependency>
</project>
Unlike pkl-config-java
, pkl-config-java-all
is a fat Jar with renamed third-party packages to avoid version conflicts.
Usage
Consuming Configuration
The ConfigEvaluator
class loads and evaluates Pkl modules.
If evaluation succeeds, a Config
object is returned.
Otherwise, a PklException
with error details is thrown.
The returned Config
object represents the root of the Pkl configuration tree.
Intermediate and leaf nodes are also represented as Config
objects.
Config
objects offer methods to
-
convert their Pkl value to a Java value of the specified type.
-
navigate to child nodes.
Let’s see this in action:
Config config;
try (var evaluator = ConfigEvaluator.preconfigured()) { (1)
config = evaluator.evaluateText(
"pigeon { age = 5; diet = \"Seeds\" }"); (2)
}
var pigeon = config.get("pigeon"); (3)
var age = pigeon.get("age").as(int.class); (4)
var diet = pigeon.get("diet").as(JavaType.listOf(String.class)); (5)
1 | Create a preconfigured ConfigEvaluator .
To create a customized evaluator, start from ConfigEvaluatorBuilder.preconfigured() or ConfigEvaluatorBuilder.unconfigured() .
The evaluator should be closed once it is no longer needed.
In this example, this is done with a try-with-resources statement.
Note that objects returned by the evaluator remain valid after calling close() . |
2 | Evaluate the given text.
Other evaluate methods read from files, URLs, and other sources.
If evaluation fails, an PklException is thrown. |
3 | Navigate from the config root to its "pigeon" child. |
4 | Navigate from "pigeon" to "age" and get the latter’s value as an int .
If conversion to the requested type fails, a ConversionException is thrown. |
5 | Navigate from "pigeon" to "diet" and get the latter’s value as a List<String> .
Note the use of JavaType.listOf() for creating a parameterized type literal.
Similar methods exist for sets, maps, and other generic types. |
A ConfigEvaluator
caches module sources and evaluation results.
To clear the cache, for example to evaluate the same module again, close the evaluator and create a new one.
For a ready-to-go example with full source code, see config-java in the pkl-jvm-examples repository.
Object Mapping
When a Config
object needs to convert its Pkl value to a Java value, it delegates the conversion to ValueMapper
.
ValueMapper
can convert an entire PModule
or any part thereof.
A ValueMapper
instance can be configured with many different Pkl-to-Java value conversions.
ValueMapper.preconfigured()
creates an instance configured with conversions from Pkl values to:
-
Number types
-
Strings
-
Enums
-
Collections
-
Arrays
-
java.util.Optional
-
java.time.Duration
-
java.net.URI/URL
-
etc.
Additionally, a preconfigured ValueMapper
instance can convert Pkl objects to Java objects with equally named properties that are settable through a constructor.
This conversion works as follows:
-
Find the Java class constructor with the highest number of parameters.
-
Match constructor parameters with Pkl object properties by name.
Unmatched constructor parameters result in a conversion error. Unmatched Pkl object properties are ignored.
-
Convert each Pkl property value to the corresponding constructor parameter’s type.
-
Invoke the constructor.
The Pkl object’s runtime type is irrelevant to this conversion. Hence, typed and dynamic Pkl objects are equally supported.
To perform this conversion, ValueMapper
needs a way to obtain the Java constructor’s parameter names.
They need to be provided in one of the following ways:
-
Annotate constructor with
java.beans.ConstructorProperties
. -
Annotate parameters with
Named
. -
Annotate parameters with
javax.inject.Named
. -
Set the Java compiler flag
-parameters
.
For a complete object mapping example, see:
Together with code generation, object mapping provides a complete solution for consuming Pkl configuration as statically typed Java objects. Java code never drifts from the configuration structure defined in Pkl, and the entire configuration tree can be code-completed in Java IDEs. |
Value Conversions
The Pkl-to-Java value conversions that ship with the library are defined in Conversions
(for individual conversions) and ConverterFactories
(for families of conversions).
To implement and register your own conversions, follow these steps:
-
For conversions from a single source type to a single target type, implement a
Conversion
.Example:
Conversions.pStringToCharacter
converts a single-characterpkl.base#String
tojava.lang.Character
. -
For conversions from one or multiple source types to one or multiple target types, implement a
ConverterFactory
.Example:
ConverterFactories.pCollectionToCollection
converts anypkl.base#Collection
to any implementation ofjava.util.Collection<E>
, for anyE
.Converter factories are called once per combination of source type and (possibly parameterized) target type. The returned `Converter`s are cached.
-
Create a
ValueMapperBuilder
, add all desired conversions, and build aValueMapper
. -
Either use the
ValueMapper
directly, or connect it to aConfigEvaluator
throughConfigEvaluatorBuilder
.