Gradle call constructor on extension - gradle

I am writing a custom plugin for Gradle. I want to be able to have:
serviceDependencies {
service name: 'service1', version: '1.0'
service name: 'service2', version: '1.1'
}
In my Plugin implementation (in Java) I have:
public void apply(final Project project) {
project.getExtensions().create("serviceDependencies", Services.class);
project.getExtensions().create("service", Service.class);
}
And Service.java:
public class Service {
private String name;
private String version;
public Service(final String name, final String version) {
this.name = name;
this.version = version;
}
public String getName() {
return this.name;
}
public void setName(final String name) {
this.name = name;
}
public String getVersion() {
return this.version;
}
public void setVersion(final String version) {
this.version = version;
}
}
When I try use this plugin I get:
java.lang.IllegalArgumentException: Could not find any public constructor for class com.xxx.xxx.Service_Decorated which accepts parameters [].
This still happens when I remove serviceDependencies/Services.java from the picture.
If I remove the Service constructor or remove the arguments.
org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException: Could not find method service() for arguments [{name=service1, version=1.0}] on root project ...
Obviously my pojo is being decorated, but not quite with the correct constructor. How can I get the constructor to work how I want in my build.gradle script?
A second and independent question is what should Services.java look like?

I would only register one extension for serviceDependencies {} that exposes functions to register your services:
public class Services {
void service(String name, String version) { /* new Service(...) */ }
}
project.getExtensions().create("serviceDependencies", Services.class);
This would allow Java, Kotlin and Groovy consumer to do something like:
serviceDependencies {
service 'service1', '1.0'
service 'service2', '0.1'
}
Then if you want to support Groovy named arguments you'd need to add:
public class Services {
void service(String name, String version) { /* new Service(...) */ }
void service(Map<String, String> namedArguments) {
service(namedArguments.get("name"), namedArguments.get("version"))
}
}
This would allow Groovy consumers to do:
serviceDependencies {
service name: 'service1', version: '1.0'
service name: 'service2', version: '0.1'
}

Related

Gradle - create custom task with command line parameters

I'm looking for a way to pass a parameter to a custom Gradle task. (Gradle 5.1.1)
I have created a task:
public class MyTask extends DefaultTask {
private String url;
#Optional
public void setUrl(String url) {
this.url = url;
}
#Input
public String getUrl() {
return url;
}
#TaskAction
public void run() {
System.out.println("URL IS " + getUrl());
}
}
This is similar to the example given at:
https://docs.gradle.org/current/userguide/custom_tasks.html#example_declaring_a_command_line_option
On the next step i created a plugin which hosts this task.
public class MyPlugin implements Plugin<Project>, TasksProducingPlugin {
#Override
public void apply(Project project) {
MyTask myTask = project.getTasks().create("exampleTask", MyTask.class);
myTask.setDescription("...");
myTask.setGroup(TASK_GROUP_NAME);
myTask.run();
#Override
public Collection<String> taskNames() {
Collection<String> collection = new HashSet<>();
collection.add("exampleTask");
return collection;
}
}
Now i want to run the exampleTask from the command line and provide the value for the url.
I try:
gradlew exampleTask --url=http://www.google.com/
And get:
Unknown command-line option '--url'.
Also tried:
gradlew exampleTask -Purl=http://www.google.com/
And get:
No value has been specified for property 'url'.
What am i doing wrong?
Look at the documentation again. You have annotated your setter method with #Optional instead of #Option.

Read multiple properties file in one go using Spring Boot?

I went through the link: How to pass a Map<String, String> with application.properties and other related links multiple times, but still its not working.
I'm using Spring Boot and Spring REST example. Link Question: How to by default execute the latest version of endpoint in Spring Boot REST?.
I've created mapping something like this and simply read the mapping
get.customers={GET: '/app-data/customers', VERSION: 'v1'}
post.customers={POST: '/app-data/customers', VERSION: 'v1'}
get.customers.custId={GET: '/app-data/customers/{custId}', VERSION: 'v2'}
Code:
private String resolveLastVersion() {
// read from configuration or something
return "2";
}
Code:
#Component
#ConfigurationProperties
#PropertySource("classpath:restendpoint.properties")
public class PriorityProcessor {
private final Map<String, String> priorityMap = new HashMap<>();
public Map<String, String> getPriority() {
return priorityMap;
}
}
Code:
I suggest the following implementation:
#ConfigurationProperties(prefix="request")
public class ConfigurationProps {
private List<Mapping> mapping;
public List<Mapping> getMapping() {
return mapping;
}
public void setMapping(List<Mapping> mapping) {
this.mapping = mapping;
}
}
Class Mapping will denote the information about the single mapping:
public class Mapping {
private String method;
private String url;
private String version;
public Mapping(String method, String url, String version) {
this.method = method;
this.url = url;
this.version = version;
}
public Mapping() {
}
// getters setters here
}
On the Configuration or spring boot application class (the one with main method):
#EnableConfigurationProperties(ConfigurationProps.class)
In the properties file put:
request.mapping[0].method=get
request.mapping[0].url=/customers
request.mapping[0].version=1
request.mapping[1].method=post
request.mapping[1].url=/students
request.mapping[1].version=2
In Filter (I assume you followed my suggestion from the linked question):
#Component
#Order(1)
public class LatestVersionFilter implements Filter {
private List<Mapping> mappings;
public LatestVersionFilter(ConfigurationProps props) {
this.mappings = props.getMapping();
}
}

Spring Boot Yaml configuration: list of typed properties

I'm following the 24.8.3 Merging Complex Types section of Spring Boot's 24. Externalized Configuration documentation.
I have this config.yaml file:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
The Properties file looks like this:
#ConfigurationProperties("acme")
#YamlPropertySource(value = { "classpath:/config.yaml" })
public class AcmeProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
The MyPojo class:
public class MyPojo {
private String name;
private String description;
public MyPojo(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The test, which fails, looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { AcmeProperties.class })
public class AcmePropertiesTest {
#Autowired
private AcmeProperties properties;
#Test
public void getOpScoringClusters() {
Assert.assertEquals(2, properties.getList().size()); // FAIL!
}
}
Spring Boot version 1.5.6.
Basically I want to have a list of typed properties. What am I doing wrong?
Several comments have highlighted multiple issues with the code presented.
Firstly, the fields inside a configuration properties can't be final as spring uses the setter to set the value.
Secondly, #YamlPropertySource is not something provided by spring so won't do anything in this context.
Thirdly, even if you did use the spring PropertySource annotation, unfortunately you can't use it with yaml files.
YAML files cannot be loaded by using the #PropertySource annotation.
I've created a sample project that uses the code you presented and has been modified so that it passes the unit test. It's using spring boot 2.x instead of 1.x but the only significant difference should be the annotations used in the test class.
https://github.com/michaelmcfadyen/spring-boot-config-props-demo

How to implement dynamic #ConfigurationProperties Prefix

I have a requirement to pass dynamic environment name as a prefix of configuration property. I will pass environment as VM argument from command line and all properties should be loaded for that environment.
My Configuration:
#Configuration
#EnableConfigurationProperties
#PropertySource("environmentDetails.yml")
#ConfigurationProperties(prefix="${environment}")
public class ConfigurationBean {
private String brokerUrl;
private String queueName;
private String receiverUserName;
private String receiverPassword;
public String getBrokerUrl() {
return brokerUrl;
}
public void setBrokerUrl(String brokerUrl) {
this.brokerUrl = brokerUrl;
}
public String getQueueName() {
return queueName;
}
public void setQueueName(String queueName) {
this.queueName = queueName;
}
public String getReceiverUserName() {
return receiverUserName;
}
public void setReceiverUserName(String receiverUserName) {
this.receiverUserName = receiverUserName;
}
public String getReceiverPassword() {
return receiverPassword;
}
public void setReceiverPassword(String receiverPassword) {
this.receiverPassword = receiverPassword;
}
}
environmentDetails.yml
spring:
profiles.active: default
---
spring:
profiles: default
environment:
brokerUrl: http://ip:port
queueName: testQueue
receiverUserName: testuser
receiverPassword: password
Here is the issue: You can't use .yml with #PropertySource: boot-features-external-config-yaml-shortcomings
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
You'll have to convert to .properties to do this.

dynamically generate list of beans from .yaml file in confg-server

I'm using spring-config-server. I have it working, but Ideally, I'd like to generate a list of servers in a .yaml file that have properties.
#Resource
private List<Server> servers;
then:
#Component
public class Server {
#Value("${server.name}")
private String name;
}
in the (applicationName).yaml file:
servers:
-
name: test
-
name: test2
See I want a List<Server> generated dynamically from a config. The fact that this config is on a config server shouldn't be that different from a local .yaml file right?
Thanks for any help
we figured this out...
Trinity:
test: Goober
servers:
-
name: test
jmxURL: jmx://test
-
name: test2
jmxURL: jmx://test
that's the config (in config server)... this is the code
#Component
#EnableAutoConfiguration
#EnableConfigurationProperties
#ConfigurationProperties(prefix="Trinity")
public class ConfigFetcher {
List<Server> servers;
public List<Server> getServers() {
return servers;
}
public void setTest(String test) {
this.test = test;
}
public void setServers(List<Server> servers) {
this.servers = servers;
}
#EnableConfigurationProperties
#ConfigurationProperties(prefix="Trinity.servers")
public static class Server{
private String name;
private String jmxURL;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJmxURL() {
return jmxURL;
}
public void setJmxURL(String jmxURL) {
this.jmxURL = jmxURL;
}
#Override
public String toString() {
return "Server [name=" + name + ", jmxUrl=" + jmxURL + "]";
}
}
}
in the main class (in my service in this case):
with the class having the following annotation
#EnableAutoConfiguration
#Autowired
private ConfigFetcher c;

Resources