Code Monkey home page Code Monkey logo

owner's Introduction

OWNER

OWNER, an API to ease Java property files usage.

Build Status Coverage Status security status stability status Built with Maven

INTRODUCTION

The goal of OWNER API is to minimize the code required to handle application configuration through Java properties files.

Full documentation available on project website.

BASIC USAGE

The approach used by OWNER APIs, is to define a Java interface associated to a properties file.

Suppose your properties file is defined as ServerConfig.properties:

port=80
hostname=foobar.com
maxThreads=100

To access this property you need to define a convenient Java interface in ServerConfig.java:

public interface ServerConfig extends Config {
    int port();
    String hostname();
    int maxThreads();
}

We'll call this interface the Properties Mapping Interface or just Mapping Interface since its goal is to map Properties into a an easy to use piece of code.

Then, you can use it from inside your code:

public class MyApp {
    public static void main(String[] args) {
        ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
        System.out.println("Server " + cfg.hostname() + ":" + cfg.port() +
                           " will run " + cfg.maxThreads());
    }
}

But this is just the tip of the iceberg.

Continue reading here: Basic usage.

DOWNLOAD

Public Releases can be downloaded from GitHub Releases page or Maven Central Repository.

DOCUMENTATION

Make sure to have a look at the documentation on project website to learn how flexible and powerful OWNER is, and why you may need it!

Chinese documentation is provided by Yunfeng Cheng via a GitHub independent project at this address.

LICENSE

OWNER is released under the BSD license. See LICENSE file included for the details.

owner's People

Contributors

andreipet avatar arturdryomov avatar bbossola avatar da-z avatar dependabot[bot] avatar drapostolos avatar fdiotalevi avatar gdenning avatar jonn-smith avatar kevin-canadian avatar ksaritek avatar lgtm-com[bot] avatar lucalas avatar lviggiano avatar matteobaccan avatar mend-bolt-for-github[bot] avatar nixxed avatar raulcaj avatar renovate-bot avatar renovate[bot] avatar robinmeiss avatar rrialq avatar stfs avatar svetanesterenko avatar toyg avatar ysmartin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

owner's Issues

Nested collections/arrays

Related to #2: support for collections (or arrays) of complex types. E.g.

public interface ServerConfig extends Config {
  String host();
  int port();
}

public interface AppConfig extends Config {
  ServerConfig[] servers(); // could also be List<ServerConfig> or Map<String, ServerConfig>
}

and in the properties file

servers.1.host=www.google.com
servers.1.port=80
servers.2.host=www.github.com
servers.2.port=80

should produce an array of 2 servers (or map with keys "1" and "2").

Automatic reload for configuration files.

It is not uncommon for some application to have a consistent and efficient "hot reload" for configuration files. At the moment, the configuration is loaded when the create method is invoked, and no cache is implemented; so it would not be hard for a user to implement some reload mechanism from outside the API. But, things would be better if the reload is triggered by file change, transparently. This should of course be kept thread safe to avoid inconsistent states.

link to #5

Properties values variables expansion

Implement variable substitution (multi-level) for properties values

Example:

public interface ConfigWithSubstitutionAnnotations extends Config {

    @DefaultValue("The ${animal} jumped over the ${target}")
    String story();

    @DefaultValue("quick ${color} fox")
    String animal();

    @DefaultValue("${target.attribute} dog")
    String target();

    @Key("target.attribute")
    @DefaultValue("lazy")
    String targetAttribute();

    @DefaultValue("brown")
    String color();
}

@Test
public void testConfigWithSubstitutionAnnotation() {
    ConfigWithSubstitutionAnnotations conf = ConfigFactory.create(ConfigWithSubstitutionAnnotations.class);
    assertEquals("The quick brown fox jumped over the lazy dog", conf.story());
}

It must work also with Config based on properties files:

story=The ${animal} jumped over the ${target}
animal=quick ${color} fox
target=${target.attribute} dog
target.attribute=lazy
color=brown

alias ConfigFactory.create ConfigFactory.get

That's it, maybe ConfigFactory.get is a better name for ConfigFactory.create.

All in all, very interesting library and a well done one. Thank you, I would definitely use it for my next projects.

Oh, I just thought of a feature like generating an application specific secret, than use it to encrypt things like password and integrate into owner so there would be no implicit decryption logic.

implement list()

The methods list(PrintWriter) and list(PrintStream) method can be proxied from Config interface to java.util.Properties.

It is especially useful when many properties files get merged (via overrides, see issue #1 ) with others: it may become not easy to understand the resulting value. So I will need to provide a method like list().

Config methods with multiple `@Key`s and `@DefaultValue`s

At the moment, the @Key annotation only accept one value (as well the @DefaultValue annotation), it would be nice if for a single property we can specify multiple @Keys and multiple @DefaultValues.

Example

interface MyConfig extends Config {
    @Key({"db.driver", "db.url", "db.user", "db.password"})
    @DefaultValue({"org.h2.Driver", "jdbc:h2:~/.myapp/data", "scott", "tiger"})
    ConnectionPool connectionPool()
}

public class ConnectionPool {
    ConnectionPool(String driverClassName, String url, String user, String pwd) { ... }
    // or... better (see issue #44)  
    ConnectionPool(Class driver, String url, String user, String pwd) { ... }

    Connection getConnection() { ... }
}

Imported properties have lower priority than the resources associated to the Config object or by @Sources

The behavior of OWNER is that imported properties have lowest priority over the one associated to the Config object or the ones specified by the @Sources annotation.

I thought that this was the best behavior, but actually I felt that there was no reasonable reason to why I decided it (I also wrote a junit test to fix this behavior as "correct").

Now reading an email I just received, I feel like the use case specified by Ivan makes sense, and it is also not an uncommon thing to do. See email below.

So I wrote a test to reproduce the bug and to redefine the correct behavior and keep it safe from regressions, see 3178f1a:

    /*
    src/test/resources/org/aeonbits/owner/ImportConfigTest$ImportedPropertiesHaveHigherPriority.properties 
    minAge=18
    */

    interface ImportedPropertiesHaveHigherPriority extends Config {
        Integer minAge();
    }

    @Test
    public void testImportedPropertiesShouldOverrideSources() {
        ImportedPropertiesHaveHigherPriority cfg = ConfigFactory.create(ImportedPropertiesHaveHigherPriority.class);
        assertEquals(Integer.valueOf(18), cfg.minAge());

        ImportedPropertiesHaveHigherPriority cfg2 = ConfigFactory.create(ImportedPropertiesHaveHigherPriority.class,
                new Properties() {{
                    setProperty("minAge", "21");
                }},

                new Properties() {{
                    setProperty("minAge", "22");
                }}

        );

        assertEquals(Integer.valueOf(21), cfg2.minAge());
    }

I already rolled out version 1.0.3.1 as a bugfix release, and I am waiting for a feedback from Ivan before to promote the version to the maven central repository.

Received a mail from Ivan G.

Hi Luigi,

Basically, I have an executable JAR containing a PROPERTIES file inside some resources folder. This is handled by Maven, so after the build is complete, BenchmarkConfig.properties will be in the same folder as BenchmarkConfig.java. The structure is somewhat as follows:

src/
  main/
    java/
      SomeDriver.java
      BenchmarkConfig.java
    resources/
      BenchmarkConfig.properties

I intend to run the JAR file as follows:

Usage:  
  java -jar some.jar <properties-path>

Example:
java -jar some.jar --
Uses the default properties file bundled inside the JAR.
java -jar some.jar SomeOtherConfig.properties
Uses the specified properties file to completely override
the default one.

However, when I try to run my JAR with the second method, there seems to be no option to completely override the default properties file when I do the following call:

1   try {
2       Properties props = new Properties();
3       props.load(new FileInputStream("path/to/SomeOtherConfig.properties");
4 
5       /* Can't do this, because it will find the key-value pair from the default
6        * properties file (BenchmarkConfig.properties) first, and not even bother
7        * searching from SomeOtherConfig.properties
8        */
9       BenchmarkConfig cfg = ConfigFactory.create(BenchmarkConfig.class, props); 
10
11  } catch (IOException e) {}

When I do the above, even when I specified a different properties file (someOtherPropertiesPath) and passed it in through props on line 6, it will still find the key-value pair from the default properties file.

I don't want this behavior. Instead, what I want is, when I pass in the props argument on the create() call, I want to completely override the entire default properties file with the file I've specified.

Is there a way to do this in your framework? I've only seen the @LoadPolicy(FIRST) and @LoadPolicy(MERGE), but I don't think those two seem fit for my purpose.

Different sources for configuration (file formats)

At the moment, the only supported format for configuration files handled by owner api is java Properties. It would be nice to have a plugin based architecture that adds support for different file formats.

Apache commons configuration provides support for several file formats.

While Java properties can be provided out of the box, without dependencies, it would be good to implements "plugins" depending on commons configuration to add the supported file formats transparently, keeping all other features unvaried.

In my opinion it is important to not add transitive dependencies in the current apache module; so I would implement the additional file formats inside different maven module(s).

Nested Properties

It could be interesting to think about some structure (i.e. nesting)

interface Foo extends Config {

    String fooProp();

    interface Bar extends Config {
        barProp();
    }

    @Key("bar");
    Bar bar();
}

So, to access barProp, the default key in the properties file would be foo.bar.barProp.
Method invocation will then be Foo.bar().barProp().

Not sure it is a good thing to have. Implementation may not be trivial. We can discuss it.

RFE: Smart properties save() method

I use properties files as config files in Gitblit so your project is very interesting to me. Issue 15 (hot reload) is definitely a requirement for me to adopt your library, but another thing I would be missing is an enhanced write feature that replaces a property value in the file without destroying the file. I currently document config settings within the properties file (example https://github.com/gitblit/gitblit/blob/master/distrib/gitblit.properties). Unfortunately, the stock Properties class destroys the source file when you save. It is not very smart. :(

To work around this I implemented a smarter save. You can see my implementation of this here: https://github.com/gitblit/gitblit/blob/master/src/com/gitblit/FileSettings.java#L86

That capability is actually pretty small and I think would make a nice addition to your library.

Provide a toString() method

Please provide a toString() method in your proxy so that I can see the content of the config and I can also use it in a log message easily. Something like:

{"max.threads":25, "max.folders":99, "default.name": "untitled"}

Add the possibility to disable variable expansion and parametrized formatting

An annotation could be used to specify the context (on class level or on method level), and it should be good enough to express the will of the programmer to disable one or both of those features.

Example:

@DisableFeature({VARIABLE_EXPANSION, PARAMETER_FORMATTING})
public static String someMethod(String p1, String p2);

// or
@DisableVariableExpansion
public class FooBar {...}

Tool to generate properties file from Config sub-interfaces

Since one can have Config interfaces with @DefaultValues without using properties files, it would be useful to have a command line tool (or a maven mojo) that checks all the classes in a project and generate properties files with the default values, so that it would be easier to change those files and/or use them as template for changing the configutation.

Who uses OWNER?

I would like to know who uses this API, and how many downloads have been made?

Add a JMX mBean for each Property

It would be awesome to automatically manage a JMX mBean for every property created / setted using your library.

We would have full control over configuration in the JDK jVisualVM for hot-modification.

Objects are not serializable

Version: 1.0.4.1

I am using OWNER framework with Twitter/Storm. Such frameworks requires all instance variables should be serializable. Could you please make all objects you define be serializable?

@Prefix annotation on interfaces for keys

I'd rather write:

@Prefix("foo.bar")
interface Shorter extends Config {
    int a();
    String b();
}

Than:

interface Longer extends Config {
    @Key("foo.bar.a")
    int a();
    @Key("foo.bar.b")
    String b();
}

Especially as @key doesn't seem to like @key("${prefix}.baz"). (But please consider @Prefix first! Or @KeyPrefix!)

Extend Converters to cover more Object types

At the moment the converters are able to instantiate objects having a single parameter constructor accepting a String or Object. But since the Converter itself is able to convert String to primitive (and non-primitive) types, it should not be difficult to extend it to cover Object with a single parameter constructor accepting any type already covered by the Converters.

Custom Types conversion

CUSTOM & SPECIAL RETURN TYPES

Since version 1.0.2 it is possible to have configuration interfaces to declare complex return types or even custom ones.

Example:

public interface SpecialTypes extends Config {
    @DefaultValue("foobar.txt")
    File sampleFile();

    @DefaultValue("http://owner.aeonbits.org")
    URL sampleURL();

    @DefaultValue("example")
    CustomType customType();

    @DefaultValue("Hello %s!")
    CustomType salutation(String name);
}

The user can define his own class types as CustomType in the previous example. The return type needs to be a public
class declaring a public constructor with a single argument of type `java.lang.String'.

Example:

public class CustomType {
    private final String text;

    public CustomType(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

If any error happens during the constructor call you'll receive a java.lang.UnsupportedOperationException.

Example:

java.lang.UnsupportedOperationException: Cannot convert 'example' to org.aeonbits.owner.CustomType
    at org.aeonbits.owner.PropertiesInvocationHandler.convert(PropertiesInvocationHandler.java:108)
    at org.aeonbits.owner.PropertiesInvocationHandler.resolveProperty(PropertiesInvocationHandler.java:72)
    at org.aeonbits.owner.PropertiesInvocationHandler.invoke(PropertiesInvocationHandler.java:63)
    at $Proxy4.customType(Unknown Source)
    ... 31 more
Caused by: java.lang.NoSuchMethodException: org.aeonbits.owner.CustomType.<init>(java.lang.String)
    at java.lang.Class.getConstructor0(Class.java:2721)
    at java.lang.Class.getConstructor(Class.java:1674)
    at org.aeonbits.owner.PropertiesInvocationHandler.convert(PropertiesInvocationHandler.java:105)
    ... 38 more

The exception description states that OWNER failed to convert the text 'example' to org.aeonbits.owner.CustomType and
the cause, in this case is because OWNER was unable to find the public constructor with a single String parameter.

You can also register your custom PropertyEditor to convert text properties into your objects
using the static method PropertyEditorManager.registerEditor().
See also PropertyEditorSupport, it may be useful if you want to implement a PropertyEditor.

Add support for YAML and JSON loaders

Given that a properties file is not nearly as convenient to edit as a YAML or JSON file, support to load configuration in these formats would definitely be a good thing.

In my case, it would make the choice of owner to manage application settings a no-brainer.

add method/class addRollbackListener()

When a rollback happens, a RollbackListener class should receive the notification of the RollbackEvent, which also include the event generating the rollback (like a PropertyChangeEvent or a ReloadEvent)

The user can intercept rollback event and do something useful for the application (i.e. print some log information)

Google App Engine Compatibility Issue

It looks like there is some issue with the Google App Engine:

java.lang.NoClassDefFoundError: java.net.URLStreamHandler is a restricted class. Please see the Google  App Engine developer's guide for more details.
        at com.google.appengine.tools.development.agent.runtime.Runtime.reject(Runtime.java:51)
        at org.aeonbits.owner.ConfigURLStreamHandler.<init>(ConfigURLStreamHandler.java:40)
        at org.aeonbits.owner.PropertiesManager.<init>(PropertiesManager.java:66)
        at org.aeonbits.owner.ConfigFactory.create(ConfigFactory.java:43)

(the above stacktrace and line numbers may relate to version 1.0.2 or 1.0.3)

sounds like a security policy: http://stackoverflow.com/questions/15673942/urlstreamhandler-is-a-restricted-class

The bug is reported to the GAE:
https://code.google.com/p/googleappengine/issues/detail?id=1384

From my point of view, this bug should be fixed in the GAE, but it would be nice to implement a workaround in the OWNER library.

Merge multiple properties configuration files from different sources.

It would be interesting to have a @SourceLoadPolicy annotation that defines a different policy to load the sources.

At the moment the @sources is scanned sequentially and the first resource available is used to load the properties.

But it would be nice to have the possibility to have an overriding mechanism:

Example:

  1. I have a property in the jar
  2. I have a global configuration under /etc/myapp.config
  3. I have a user-level configuration under ~/.myapp/config

The @SourceLoadPolicy("override") whould resolve a call config.someProperty() looking for someProperty inside user-level configuration first, then on the global configuration and finally in the jar resource (or the interface itself, that defines the @DefaultValue).

Support for Map objects

sample property:

something.foo=1
something.bar=2
something.baz=3

sample class:

interface MyConfig extends Config {
    Map<String, Integer> something(); 
}

something() should return a map containing: foo=1, bar=2, baz=3

Substitution and format not working as expected when used together

This test is failing in version 1.0.3

public static interface SubstituteAndFormat extends Config {
    @DefaultValue("Hello ${mister}")
    String salutation(String name);

    @DefaultValue("Mr. %s")
    String mister(String name);
}

@Test
public void testSubstitutionAndFormat() {
    SubstituteAndFormat cfg = ConfigFactory.create(SubstituteAndFormat.class);
    assertEquals("Hello Mr. Luigi", cfg.salutation("Luigi")); // this fails returning "Hello Mr. %s"
    assertEquals("Mr. Luigi", cfg.mister("Luigi"));
}

Improvements on javadocs and online documentation

Things that need documentation (javadoc and readme.md) improvements:

  • new interface Listable
  • new interface Reloadable
  • owner website needs to be rewritten. The single page layout is not scaling with the addition of new features.

as in subject. I'll keep things here that need documentation improvements, until release.

implement list()

List() method can be proxied from java.util.Properties.

It is especially useful when many properties files get merged (via overrides, see issue #1 ) with others: it may become not easy to understand the resulting value. So I will need to provide a method like list().

@Required

It would be nice if OWNER supports the possibility to declare an option as required, maybe with an annotation called @required. If no value was assigned to such an option, OWNER should throw an exception and gives the possibility to write a nice overview of options that are required to a given OutputStream.

What do you think?

Properties values runtime modifications

From a private email:

I would say that the main feature I am looking forward to is changing the value of a property at runtime without editing the file.
The reason is that I'm coding a server that is deployed on many environments, each having its own unique values for many properties.
I have :

  • a default value or not, depending if it makes sense for every environment,
  • a specific starting value (per environment) written in the file,
  • a "hot" value, that I sometimes want to apply at runtime (the change may be temporary, so I might want to change it back few minutes/hours later).

This issue is also related to #19 since changing properties values at runtime, it can be nice to have a jmx bean for that too.

@ConverterClass annotation

As per discussion in issue #37

Given a properties file like:

servers=www.google.com:80, www.github.com:8080

and a Java source like

public interface ServerConfig extends Config {
    Server[] servers;
    // or 
    List<Server> servers;
}

public class Server {
    private final String name;
    private final Integer port;

    public Server(String name, Integer port) {  
        this.name = name; 
        this.port = port;
    }
    public String getName() { return name; }
    public Integer getPort() { return port; } 
}

public interface ServerConfig extends Config {
    @ConverterClass(ServerConverter.class)
    Server[] servers;
    // or 
    List<Server> servers;
}

public class ServerConverter extends Converter<Server> {
    public Server convert(Method targetMethod, String text) {
        String[] split = text.split(":", -1);
        String name = split[0];
        Integer port = 80;
        if (split.lenght >= 2) 
            port = Integer.valueOf(split[1]);
        return new Server(name, port);
    }
}

The @ConverterClass annotation should allow the user to specify a class specific to a method to convert the associated property to the resulting object.

It should work also for collections and lists as in the above example.

implement list()

List() method can be proxied from java.util.Properties.

It is especially useful when many properties files get merged (via overrides, see issue #1 ) with others: it may become not easy to understand the resulting value. So I will need to provide a method like list().

Support for indexed keys

I would like to request an option to include duplicate keys or keys with index. The config should return list/array for all the values for this key.

e.g.
server=Sometext
server=Othertext

OR
server.0=Sometext
server.1=Othertext

@key("server")
String[] server;

Thanks,

Add a method to detect which properties were changed in a reload

The HotReload is super cool :) however I think that generally it would be nice to know which properties has changed so that you can re-configure only the affected services, It can be embedded into the ReloadEvent, or it could be possible have a helper method (like I have in my code now) that is able to do a "diff" in the properties for me.

Allow a non-static ConfigFactory

At the moment ConfigFactory is static, so if somebody configures it using ConfigFactory.setProperty(), this affect the singleton object (across the VM). It would be nice to have a non static ConfigFactory. i.e. ConfigFactory.newInstance() that allows configuration for just that instance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.