New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Q: how to support nested collections with unknown number of elements #126
Comments
Here I post my solution to this problem. It is built using variable expansion and type conversion. @Sources({"file:config.txt"})
public interface MyConfig extends Config {
@Separator(",")
@Key("servers.list")
@ConverterClass(ServerConverter.class)
List<Server> servers();
}
@Sources({"file:config.txt"})
public interface Server extends Config {
@Key("server.${server}.host")
String host();
@Key("server.${server}.port")
int port();
@Key("server.${server}.login")
String login();
}
public class ServerConverter implements Converter<Server> {
@Override
public Server convert(Method method, String input) {
Map<String, String> imports = new HashMap<String, String>();
imports.put("server", input);
Server server = ConfigFactory.create(Server.class, imports);
return server;
}
}
public class Main {
public static void main(String... args) {
MyConfig config = ConfigFactory.create(MyConfig.class);
List<Server> servers = config.servers();
for (Server s : servers) {
System.out.println("Host: "+s.host());
System.out.println("Port: "+s.port());
System.out.println("Login: "+s.login());
}
}
} And then you will be able to define a configuration with nested structure. Similarly, Server object may have other lists of objects and so on.. You will have to call ConfigFactory.create every time you read a list of objects but I haven't found any better way to deal with such configurations.
|
Quite brilliant I must say. 👍 |
In above example: Use an
Working example: package owner.example;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.aeonbits.owner.Accessible;
import org.aeonbits.owner.Config;
import org.aeonbits.owner.Config.Sources;
import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.Converter;
public class Main {
static final String CONFIG_FILE = "classpath:owner/example/config.txt";
@Sources({ CONFIG_FILE })
public interface MyConfig extends Config {
@Separator(",")
@Key("servers.list")
@ConverterClass(ServerConverter.class)
@DefaultValue("${server.names}") // <-- note default value
List<Server> servers();
}
@Sources({ CONFIG_FILE })
public interface Server extends Config {
@Key("server.${server}.host")
String host();
@Key("server.${server}.port")
int port();
@Key("server.${server}.login")
String login();
}
public static class ServerConverter implements Converter<Server> {
@Override
public Server convert(Method method, String input) {
Map<String, String> imports = new HashMap<String, String>();
imports.put("server", input);
Server server = ConfigFactory.create(Server.class, imports);
return server;
}
}
public static void main(String[] args) {
MyConfig config = myConfig();
List<Server> servers = config.servers();
for (Server s : servers) {
System.out.println("Host: " + s.host());
System.out.println("Port: " + s.port());
System.out.println("Login: " + s.login());
}
}
static MyConfig myConfig() {
String serverNames = parseServerNames();
Map<String, String> imports = new HashMap<String, String>();
imports.put("server.names", serverNames);
return ConfigFactory.create(MyConfig.class, imports);
}
@Sources({ CONFIG_FILE })
private interface ServerNames extends Accessible {
@Key("server.name.pattern")
@DefaultValue("server\\.(.*)\\.host")
String serverNamePattern();
}
private static String parseServerNames() {
ServerNames cfg = ConfigFactory.create(ServerNames.class);
Pattern p = Pattern.compile(cfg.serverNamePattern());
// use a set to avoid duplicates
Set<String> serverNames = new LinkedHashSet<String>();
for (String propertyName : cfg.propertyNames()) {
Matcher m = p.matcher(propertyName);
if (m.matches()) {
serverNames.add(m.group(1));
}
}
return serverNames.toString().replaceAll("^\\[", "").replaceAll("\\]$", "");
}
} |
Hi, this is a question related to nested collections or arrays of complex types. I saw already similar questions and answers, and also a possibility to use variable expansion, but I still didn't figure out how to deal with cases when amount of elements in collection is not known beforehand.
Example: list of servers (each server has host, port, login), but amount of servers not known. How to represent it with properties or with XML supported by OWNER?
server.uat.host=..
server.dev.host=...
server.xxx.host=... and so on..
First I thought to have a property, which lists all server names: server.list=uat,dev,xxx,abc,foo
And then OWNER will be able somehow to read and differentiate server.uat.host, server.dev.host, etc.., where uat,dev,etc are taken from server.list property.
The main point here is that the list of server names is not know apriori and in every configuration file it can be different. But application should be able to get configuration for all specified servers.
Is it possible to achieve with current version of OWNER?
Important aspect here is that we are talking about complex types, where there are many different attributes per server, so list "hostA:portA, hostB, hostC:portC" is not sufficient.
The text was updated successfully, but these errors were encountered: