Maven plugin: Passing all available properties to an Interpolator - maven

In a Maven plugin I would like to use all properties usually available to the pom inside an Interpolator through a ValueSource.
I can access properties defined inside the pom, system properties and environment variable properties using one of MavenProject#getProperties(), MavenSession#getProperties() or MavenProject#getModel()#getProperties. This works fine with code like that:
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
ValueSource valueSource = new PropertiesBasedValueSource( project.getModel()
.getProperties() )
However, the standard project properties like project.version or project.build.directory are not part of any of those Properties instances. How can I get all of them at once in a properties instance?
My plugin runs in the generate-sources phase using Maven 2.2.1 or 3.0.3.

Answer to my own question :-):
I want to pass all properties available normally in filters. While I could set each and every property available as a parameter to my plugin, that solution would require to modify the plugin source code and recompile it whenever new properties get added tomaven. Also, the source code of the mojo would grow to a high number of lines.
The project.* properties are not actually properties available through the Project#getProperties method when used in filters. Instead they are mapped directly to the getter methods of the project object itself. The class doing that is PrefixedObjectValueSource in the same interpolation package as PropertiesBasedValueSource. It will map each getter method (bean property) to a property, prefixing it with a defined string, and recursively exposing the bean properties of each property.
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
ValueSource allProperties = new PropertiesBasedValueSource( getProject().getModel()
.getProperties() );
ValueSource projectProperties =
new PrefixedObjectValueSource( "project.", getProject() ) );

Related

Set a ListMoveChange filter when using OptaPlanner Spring Boot starter

We are using the OptaPlanner Spring Boot starter to create a vehicle routing problem solver based on the example in the OptaPlanner quickstarts:
https://github.com/kiegroup/optaplanner-quickstarts/tree/stable/use-cases/vehicle-routing
So we do not have an solveConfig.xml file. We would like to define a filter for ListChangeMoves but it's not clear how we would register this without using an XML file. We have tried using a solverConfig.xml e.g.
<localSearch>
<unionMoveSelector>
<listChangeMoveSelector>
<filterClass>my.filter.Class</filterClass>
</listChangeMoveSelector>
</unionMoveSelector>
</localSearch>
But this is not working. Is there an example of setting up a filter for list moves?
This is a XML solver config using a a swap move selector and a change move selector with move filtering:
<constructionHeuristic/>
<localSearch>
<unionMoveSelector>
<changeMoveSelector>
<filterClass>org.acme.vehiclerouting.solver.ChangeMoveSelectorFilter</filterClass>
</changeMoveSelector>
<swapMoveSelector/>
</unionMoveSelector>
</localSearch>
If you don't want to use swap moves, then you don't need the union move selector and the configuration can be simplified to:
<constructionHeuristic/>
<localSearch>
<changeMoveSelector>
<filterClass>org.acme.vehiclerouting.solver.ChangeMoveSelectorFilter</filterClass>
</changeMoveSelector>
</localSearch>
A few comments:
I'm including the CH phase because it is necessary in a typical case. See OptaPlanner terminates immediately if I add constructionHeuristic config for an explanation.
The ChangeMoveSelector is automatically configured to produce ListChangeMoves if the planning entity has a #PlanningListVariable. There is no <listChangeMoveSelector> config element.
More information including how to implement the move selection filter can be found in the documentation.
UPDATE: No XML configuration
It's possible to inject SolverConfig, modify it and then use it to create other objects, for example Solver, SolverManager, and ScoreManager.
The code would look something like this:
#Component
class MyService {
// Don't inject these.
private final SolverManager<VrpSolution, Long> solverManager;
private final ScoreManager<VrpSolution, HardSoftScore> scoreManager;
// But inject the SolverConfig.
public MyService(SolverConfig solverConfig) {
// And instantiate SolverManager and ScoreManager manually.
this.solverManager = SolverManager.<VrpSolution, Long>create(
solverConfig.withPhaseList(Arrays.asList(
new ConstructionHeuristicPhaseConfig(),
new LocalSearchPhaseConfig().withMoveSelectorConfig(
new ChangeMoveSelectorConfig()
.withFilterClass(MyFilter.class)))));
this.scoreManager = ScoreManager.create(SolverFactory.create(solverConfig));
}
}
Pros:
SolverConfig will be initialized by OptaPlannerAutoConfiguration (from optaplanner-spring-boot-starter) before it's injected into your component. That means:
The solution and entity classes will be auto-discovered and you don't have to specify them (which you have to in solverConfig.xml).
You can use application.properties to do minor solver config tweaks, for example set time-spent termination.
Cons:
You have to create Solver,SolverManager, and ScoreManager instances manually. Specifically, you can't have a #Bean method producing an instance of one of the types above because that would deactivate OptaPlannerAutoConfiguration, which creates the SolverConfig bean.

Load Yml at runtime with Spring Boot

I have multiple yml files in different folders. All the files in the folder share the same property structure which I mapped with a java bean.
At runtime, with a factory, I want to get the right bean populated with the values of the specific file chosen at runtime. How do I do that?
Thanks
The #ConfigurationProperties annotation or the mechanism behind it is built to be used for configuration of an application at startup, not loading data at runtime.
I'm sure you could somehow start mini spring environments at runtime just to read this data using different spring profiles (this is e.g. how spring-cloud-configserver loads properties) but this seems not right and there are better alternatives.
E.g., if you need that data to be loaded at runtime, you can use jackson's yamlfactory for that, with that you can read your data in 3-4 statements. A good example is here: https://www.baeldung.com/jackson-yaml.
Consider a Bean like this: (Pseudo code, just to explain)
class MyConfigBean {
private Properties currentProperties;
private Map<String, Properties> allPropertiesMap;
void loadAllProperties() { ... }
void switchProperties(String name) {
this.currentProperties = this.allPropertiesMap.get(name);
}
String getProperty(String key) {
return this.currentProperties.get(key);
}
}
You can load all of the Yaml files into a Map in your bean. The Map's key could be the "name" of the properties file and the value would be the Properties object.
A switchProperties(String name) method will "select" the properties file you wish to work with. Using the name, you will get the appropriate Properties object from the Map and assign it to the "currentProperties" object.
This way, each time you get a property by key, it will be fetched from the "currentProperties" according to what you "switched" for.
Important - You'll have to decide what is the default properties after you load all of them.

How Does Groovy Support Adding Configuration in Gradle Configuration Script Block

In the Gradle documentation when you use a configurations block in your build.gradle file the closure that is passed delegates to a ConfigurationContainer object. A truncated form of the example usage is given below:
configurations {
//adding a configuration:
myConfiguration
}
I am used to the calls inside the closure being method calls on the delegated to object, but here myConfiguration is just a single word and I know that in Groovy a method with no parameters must have parentheses so this can't be a method call. Somehow by putting this single word in which looks to me like it should be invalid Groovy a new configuration of myConfiguration is added to the delegated to ConfigurationContainer.
How is this working?
Project.configurations(Closure) calls ConfigurationContainer.configure(Closure), which creates and adds named items to container, as they declared in closure. Because ConfigurationContainer extends NamedDomainObjectContainer, every item added to It should have name. In provided code sample only name is declared, so myConfiguration item is created and added, with default field values. In case one needs to configure item (change its properties), then configuration would look as follows:
configurations {
myConfiguration {
transitive = false
}
}
More info/insights https://gist.github.com/tlberglund/3132622.

Spring Boot EnvironmentPostProcessor overriding Command Line

I am using an EnvironmentPostProcessor, in particular the CloudFoundryVcapEnvironmentPostProcessor, in order to parse some environment variables and make them accessible as Spring properties.
When I run my application, the EnvironmentPostProcessor kicks in and creates the desired property variables as expected.
#Value("${vcap.services.test-service.name}") /* Example of a property loaded from PostProcessor. Works fine. */
However, when I try to set this property value explicitly using the command line, or properties file, the value that I specify does not override the value that is being populated by the EnvironmentPostProcessor. I would expect that overriding this property via the command line should take precedence.
vcap.services.test-service.name=TEST_VALUE Does not override.
Essentially, there seems to be nothing I can do in order to override the value set by this EnvironmentPostProcessor (command line, profiles, .properties files, spring.factories order definitions, etc)
Is there any way to override a property value created in an EnvironmentPostProcessor?
This is caused by CloudFoundryVcapEnvironmentPostProcessor adding a property source with a precedence that's higher than the method you're using to override properties: https://github.com/spring-projects/spring-boot/blob/v1.3.3.RELEASE/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java#L126-L135
There is that block that sets this lower than command line args, were you using command line args or -D system properties?
You could try adding spring-boot-starter-actuator and hit the /env endpoint to see all property sources and their precedence, those appearing first have higher precedence than those appearing further down in the JSON. As a last resort you can create your own EnvironmentPostProcessor that's Ordered to execute after CloudFoundryVcapEnvironmentPostProcessor, that creates a property source with highest precedence.

Spring #Value and dynamically modifying values at runtime (PlaceholderConfigurerSupport)

This is a question about customisation of Spring's placeholder resolution in #Value annotations.
We initialise all properties in our app using #Value, normally from servlet context init params, eg:
web.xml
<context-param>
<param-name>app.some.param</param-name>
<param-value>SOME_VALUE</param-value>
</context-param>
Class file
#Value("${app.some.param:DEFAULT_VALUE}")
private String myParameter;
We actually don't use web.xml, we use Tomcat context files or even specify using vmargs.
What we'd like to support is dynamic changes to these properties at runtime. I want to somehow collect a list of property keys that are used in #Value and which also have a new annotation like #Dynamic. For properties marked as #Dynamic the bean may provide a corresponding setter, to do any re-initialisation when the property is modified.
I would then like to create a service that supports updating the property by key, eg:
void setProperty(String key, String value) {
// find all beans that have #Value and #Dynamic and set field or call setter
// NB - should support Spring type coercion, eg. string --> integer, boolean, list, etc.
}
I've been looking at the source for PlaceholderConfigurerSupport and BeanDefinitionVisitor. It seems I might be able to override PlaceholderConfigurerSupport.doProcessProperties and create a custom BeanDefinitionVisitor, but there is quite a lot of code to wade through. I wondered if anyone had looked at this before and found a solution.
I should note that there's more we ultimately want to do. We want to persist changed properties in a backing store, and use these instead of the configuration on startup if they've been modified. In this way we'd have a hierarchy of property sources: default in code, context/property files, peristed config that's been modified. We also want to provide a UI showing a set of all dynamic properties. You get the idea.
Thanks

Resources