Skip to content
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

Command line arguments #74

Closed
ghost opened this issue Mar 11, 2014 · 9 comments
Closed

Command line arguments #74

ghost opened this issue Mar 11, 2014 · 9 comments
Assignees
Labels

Comments

@ghost
Copy link

ghost commented Mar 11, 2014

Is it possible to define command line arguments as source? If not, this would be a nice feature. What do you think?

@lviggiano
Copy link
Collaborator

Hi.

Sorry for the delay, and at the moment also github is down :(

Yes, it is possible to use the command line - or a programmatic way - to indicate from where to load the properties. See Configuring, also this question relates to #35 and #36, you can have a look to discussions and examples there.

Example:

// notice ${mypath} here
@Sources("file:${mypath}");
interface MyConfig extends Config { ... }

public static void main(String[] args) {
    String cmdLinePath = "/foo/bar/baz.properties";
    if (args.length() > 0)
        cmdLinePath = arg[0];
    ConfigFactory.setProperty("mypath", cmdLinePath);
    MyConfig cfg = ConfigFactory.create(MyConfig.class);
}

Also, instead of using command line argument, you can use system properties, which are expanded by default:

// notice ${mypath} here
@Sources("file:${mypath}");
interface MyConfig extends Config { ... }

MyConfig cfg = ConfigFactory.create(MyConfig.class);

should work from system properties by default, if you start your app with -D jvm option

java -Dmypath="/foo/bar/baz.properties" com.acme.MyApp

Both the above will also support hot reload, of the @Sources, if you need.


Another option is to use Imported Properties

Properties props = new Properties();
props.load(new FileInputStream(args[0]);

MyConfig cfg = ConfigFactory.create(MyConfig.class, props);

or use the method Mutable.load()on Mutable interface:

interface MyMutableConfig extends Mutable { ... }

MyMutableConfig cfg = ConfigFactory.create(MyMutableConfig.class);
cfg.load(new FileInputStream(args[0]));

But without using the @Sources and loading things programmatically as in the last two examples, you can't benefit of the automatic hot reload.


Let me know if this answer satisfies your use cases.

@lviggiano lviggiano self-assigned this Mar 11, 2014
@ghost
Copy link
Author

ghost commented Mar 12, 2014

Thank you for your fast response!

Sorry, i did not make myself clear enough. I don't want to tell via command line which source I want to use. I want to use the arguments (passed via commandline) themselfs as a source.

E.g. I have following interface.

public interface ImportConfig extends Config {

    @DefaultValue("apple")
    String foo();

    @DefaultValue("pear")
    String bar();

    @DefaultValue("orange")
    String baz();

}

It would be great to make it possible to overwrite configuration from property file with command line arguments.

E.g we load from following property file:

foo=melon
baz=cranberry

and we start our software with following arguments
mysoftware --baz lemon

Then the configuration object generated by owner should return following values:

foo() returns melon
bar() returns pear
baz() returns lemon

This functionality would be great, because i don't have to deal with command line arguments (with args4j) additionally all the time. It's often the case that I want to make the user able to overwrite configuration with arguments passed to command line.

What do you think about such a feature?

@lviggiano
Copy link
Collaborator

This is also possible.

The OWNER library doesn't parse command line arguments; so first, you need to use something like commons-cli and get your properties overridings inside a properties object:

Properties propsOverriding = parseCommandLinePropsOverridings();
ImportConfig cfg = ConfigFactory.create(ImportConfig.class, propsOverriding);

To parse the command line there are several options.

Or, another option is to use System properties (on the command line) to override:

// to override props define system properties at the command line 
// $ java  -Dbaz=lemon com.acme.MyApp
//
ImportConfig cfg = ConfigFactory.create(ImportConfig.class, System.getProperties());

In this way you can redefine properties at the command line.

In the same way, you can also use environment variables, with System.getenv() on place of System.getProperties().

@ghost
Copy link
Author

ghost commented Mar 18, 2014

Using commons-cli or something is not a perfect solution, because I have to define configuration options two times that are identical. Command line arguments as first class citizen in owner would be great, especially if an enhanced validation mechanism is available to output configuration errors (#57).

As far as this is not possbile, using system properties seems to be a reasonable compromise.

@lviggiano
Copy link
Collaborator

In Java, when executing a command line App, the method called is something like:

public static void main(String[] args) {
    .... 
}

So, the minimum you can do, is to pass args to the ConfigFactory. An adapter can be created to translate args into a Map, then you can do something like ConfigFactory.create(MyConfig.class, map(args)).

But I find this out of scope for the core library. So I am planning to create a new module (maven module) to collect all utilities that can simplify common tasks like this. I'll keep this request in mind for that. In this way you'll need to add an additional jar file to the classpath to have those utilities available.

A list of adapters is already in planning to allow reading configuration from web.xml, applet parameters, jndi, etc. I just updated issue #14 which is related to this.

Feel free to implement your solution and propose it as integration for OWNER.

@Doogiemuc
Copy link

I second the notion that command line options should be part of this lib.
In both forms: pass a file to read as command line parameter
or parse single command line parameteres (in long or short form) that overwrite everything else.

@Doogiemuc
Copy link

Just as an updated: I implemented this by using apache-commons-cli library together with owner. It works quite well

In your main() method:

Options opts = new Options();
opts.addOption("u", "usage", false, "Show this usage information");
opts.addOption("h", "help", false, "Show help");
opts.addOption("v", "version", false, "Prints the version information and exit");

//----- read config properties (possibly overwritten from command line args or file)
Config cfg = ConfigFactory.create(MyConfig.class, parseCommandLineArgs(args, opts));

And here is the parseCommandLineArgs() helper method:

public static Properties parseCommandLineArgs(String[] args, Options opts) {
    CommandLineParser parse = new DefaultParser();
    CommandLine commandLine = null;
    try {
      commandLine = parse.parse(opts, args, false);  // false = allow arguments not defined in opts
    } catch (ParseException e) {
      System.err.println("Cannot parse command line parameters: "+e);
      System.exit(1);
    }

    if (commandLine.hasOption("usage") || commandLine.hasOption("help")) {
      showUsage(opts);
      System.exit(0);
    }

    //------ set single properties that were passed at command line
    for (int i = 0; i < commandLine.getOptions().length; i++) {
      String key = commandLine.getOptions()[i].getLongOpt();
      propsFromCommandLine.put(key, commandLine.getOptionValue(key));
    }

    return propsFromCommandLine;
  }

@lviggiano
Copy link
Collaborator

Thanks, but I think a library should NOT interfere with user interface (as command line), unless the user wants to. Also, 3rd part libraries dependencies are not welcome without a very good reason.

@lviggiano
Copy link
Collaborator

In fact, there is no OWNER real usage in your code: this is not what owner does or it is supposed to do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants