Skip to content

Commit

Permalink
Add EZKV maven plugin.
Browse files Browse the repository at this point in the history
  • Loading branch information
agentgt committed Dec 27, 2024
1 parent 05d8252 commit bdadc96
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 0 deletions.
37 changes: 37 additions & 0 deletions ezkv-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.jstach.ezkv</groupId>
<artifactId>ezkv-maven-parent</artifactId>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>ezkv-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<properties>
<doc.resources>../</doc.resources>
<parent.root>${basedir}/..</parent.root>
<maven.javadoc.skip>false</maven.javadoc.skip>
</properties>
<dependencies>
<dependency>
<groupId>io.jstach.ezkv</groupId>
<artifactId>ezkv-kvs</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<scope>provided</scope>
<version>3.15.1</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package io.jstach.ezkv.maven;

import java.io.IOException;
import java.util.Collections;
import java.util.Properties;
import java.util.Set;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.jspecify.annotations.Nullable;

import io.jstach.ezkv.kvs.KeyValuesEnvironment;
import io.jstach.ezkv.kvs.KeyValuesSystem;
import io.jstach.ezkv.kvs.Variables;

/**
* This plugin is similar to <a href=
* "https://www.mojohaus.org/properties-maven-plugin/">properties-maven-plugin</a> except
* that it will use EZKV to load the properties. Unlike the Codehaus properties plugin
* this plugin allows chain loading of config.
* <p>
* However like the Codehaus properties plugin this plugin has the following limitations:
* </p>
* <p>
* This plugin (as all other) is executed in the later phase - when project model is
* already build in memory.
* </p>
* <p>
* So properties read from external files by this plugin can not by used in project
* definitions in items like {@code <goal>}, {@code <version>} and so on.
* </p>
* <p>
* Properties read by plugin in one module are not propagated to other modules or child
* projects.
* </p>
* <p>
* Properties are only available for other plugins in runtime like for
* maven-resource-plugin for filtering resources.
* </p>
*/
@Mojo(name = "read-config", defaultPhase = LifecyclePhase.NONE, requiresProject = true, threadSafe = true)
public class ReadConfigMojo extends AbstractMojo {

@Parameter(defaultValue = "${project}", readonly = true, required = true)
private @Nullable MavenProject project;

@Parameter(defaultValue = "${session}", readonly = true, required = true)
private @Nullable MavenSession session;

/**
* Maven will call this constructor.
*/
public ReadConfigMojo() {
}

/**
* The URLs that will be used when reading properties. These may be non-standard URLs
* of the form <code>classpath:com/company/resource.properties</code>. Note that the
* type is not <code>URL</code> for this reason and therefore will be explicitly
* checked by this Mojo.
*/
@Parameter(required = true)
private String[] urls = new String[0];

/**
* Default scope for test access.
* @param urls The URLs to set for tests.
*/
public void setUrls(String[] urls) {
if (urls == null) {
this.urls = null;
}
else {
this.urls = new String[urls.length];
System.arraycopy(urls, 0, this.urls, 0, urls.length);
}
}

private static <T> T requireInjected(@Nullable T obj, String message) throws MojoExecutionException {
if (obj == null) {
throw new MojoExecutionException("Missing injection '" + message + "'");
}
return obj;
}

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
String[] _urls = this.urls;
if (_urls == null || _urls.length < 1) {
throw new MojoExecutionException("urls not set");
}
var log = getLog();
var logger = new MavenLogger(log);
var session = requireInjected(this.session, "session");
var project = requireInjected(this.project, "project");
KeyValuesEnvironment env = new KeyValuesEnvironment() {
@Override
public Properties getSystemProperties() {
return session.getSystemProperties();
}

@Override
public KeyValuesEnvironment.Logger getLogger() {
return logger;
}
};
var loader = KeyValuesSystem.builder()
.useServiceLoader() //
.environment(env) //
.build()
.loader();

final Variables missingSystemPropeties = new Variables() {
final Set<String> requiredSystemProperties = Collections.singleton("user.home");

@Override
public @Nullable String getValue(String key) {
if (requiredSystemProperties.contains(key)) {
getLog().info("Using real system property as it was not in maven's system properties. key=" + key);
return System.getProperty(key);
}
return null;
}
};
loader.add(missingSystemPropeties);
loader.variables(Variables::ofSystemEnv);
loader.variables(Variables::ofSystemProperties);
Properties originalProperties = project.getProperties();
// TODO maybe a bad idea?
loader.add(originalProperties::getProperty);

for (String url : _urls) {
loader.add(url);
}
try {
var kvs = loader.load().memoize();
if (log.isDebugEnabled()) {
getLog().debug("Found keys: " + kvs);
}
// TODO should we do some sort of lock here?
// Technically properties is synchronized
for (var kv : kvs) {
originalProperties.put(kv.key(), kv.value());
}

}
catch (IOException e) {
throw new MojoExecutionException("failed to load config", e);
}

}

record MavenLogger(Log log) implements KeyValuesEnvironment.Logger {

@Override
public void debug(String message) {
if (log.isDebugEnabled()) {
log.debug(message);
}
}

@Override
public void info(String message) {
if (log.isInfoEnabled()) {
log.info(message);
}
}

@Override
public void warn(String message) {
if (log.isWarnEnabled()) {
log.warn(message);
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* EZKV Maven Plugin to load configuration during Maven build and is analogous to
* <a href="https://www.mojohaus.org/properties-maven-plugin/">properties-maven-plugin</a>
* except that it will use EZKV to load the properties. Unlike the Codehaus properties
* plugin this plugin allows chain loading of config.
* @see io.jstach.ezkv.maven.ReadConfigMojo
*/
@org.jspecify.annotations.NullMarked
package io.jstach.ezkv.maven;
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>${maven.core.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${maven.core.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
Expand Down Expand Up @@ -977,5 +984,6 @@
<module>ezkv-boot</module>
<module>ezkv-dotenv</module>
<module>ezkv-json5</module>
<module>ezkv-maven-plugin</module>
</modules>
</project>

0 comments on commit bdadc96

Please sign in to comment.