Make parameters available in an application - java

What is the standard way of storing a command line argument so that it can be accessed when required?
I can see 2 scenarios:
The argument is consumed immediately (A logging level)
The argument is not needed immediately (On failure send email to address X)
With scenario 1 It would seem quite natural to configure it upfront, however when it is a scenario more in the vein of scenario 2 I would prefer to configure that component as and when necessary (IE not up front)
So to phrase the question slightly differently how do I make my configuration options available to my entire application?

You can have a singleton Configuration object, in which all relevant things are stored.
public class Configuration {
private static final Configuration conf = new Configuration();
public static Configuration get() {
return conf;
}
private String failureEmailAddress;
public String getFailureEmailAddress() {
return failureEmailAddress;
}
public void parseCommandLine(String[] args) {
// ...
}
}

You can use something like that (you can store the CommandLine somewhere or use the opions right away):
Options options = createOptions();
CommandLineParser parser = new GnuParser();
CommandLine cmdLine;
int timeoutHumanMove;
try
{
cmdLine = parser.parse(options, args, true);
timeoutHumanMove = getTimeoutOption(cmdLine, "thm", 300);
}
catch(ParseException e)
{
return;
}
private static int getTimeoutOption(CommandLine cmdLine, String opt, int defaultSeconds)
throws ParseException
{
if(cmdLine.hasOption(opt))
{
Number val = (Number)cmdLine.getParsedOptionValue(opt);
return (int)(val.doubleValue() * 1000D);
} else
{
return 1000 * defaultSeconds;
}
}
private static Options createOptions()
{
Options options = new Options();
options.addOption(OptionBuilder.withDescription("print this help and exit").create(OptionBuilder.withLongOpt("help"), 104));
// ...
return options;
}

There is no standard way to store application wide configuration information such as you describe. However, most of the time you store it in a class which is specific to the job (ApplicationContext), and then the instance is passed into the other classes as parameters or in a constructor or something like that. I usually make the ApplicationContext immutable.
Some applications I've come across use a static global context, effectively a global variable. This isn't necessarily a good idea, for the same reason that you should avoid global variables.
However, I would say that you should always verify that the command line options are valid up front. You don't want to do 3 hours of processing and then find out that someone hadn't configured the email address correctly on the command line. This should be a fail-fast situation.

You could store your command args into System properties using System.setProperty() then you can access your properties anywhere via System.getProperty()..

Related

How to safely read the property file in Java?

I have a property file which is like this -
emailFrom=hello#abc.com
emailTo=world#abc.com
# can be separated by comma
whichServer=UserServer,GuestServer
maxTestInSec=120
numberOfUsers=1000
Now I am reading this property file like this in Java which works if everything is set properly -
private static final Properties prop = new Properties();
private static String emailFrom;
private static String emailTo;
private static List<String> whichServer;
private static String maxTestInSec;
private static String numberOfUsers;
public static void main(String[] args) {
readConfig(args);
}
private void readConfig(String[] args) throws FileNotFoundException, IOException {
if (!TestUtils.isEmpty(args) && args.length != 0) {
prop.load(new FileInputStream(args[0]));
} else {
prop.load(TestTask.class.getClassLoader().getResourceAsStream("config.properties"));
}
emailFrom = prop.getProperty("emailFrom").trim();
emailTo = prop.getProperty("emailTo").trim();
whichServer = Arrays.asList(prop.getProperty("whichServer").trim().split(","));
maxTestInSec = prop.getProperty("maxTestInSec").trim();
numberOfUsers = prop.getProperty("numberOfUsers").trim();
}
Problem Statement:-
I need to make sure that if any of the property value is missing then I want to use default value for that and if by any chance that property is commented out, then also I want to use default value but I would log a warning message stating the property is missing or empty so using default values. I am trying to cover all the corner cases for reading the file -
Now let's say, if I am not specifying values to any of my property in the above file, then I want to use default values for the property which I haven't provided and log as a warning stating that, no values have been provided for this property so using the default values. For example : Let's say if I haven't provided any value for emailFrom field, then I would like to use default value as hello#abc.com for that and similar thing for others. The default values for all the property will be :
emailFrom=hello#abc.com
emailTo=world#abc.com
whichServer=UserServer
maxTestInSec=30
numberOfUsers=500
Also, if any of the property is commented out then the above code is going to through NPE exception. How can I use default values in that scenario as well?
Should I start using Command Line parser for this? What is the best and clean way to handle these stuffs?
I don't want to have lot of if blocks to add a check and then set the default values.
As of Java 8 the easiest thing to do is use getOrDefault() which lets you specify a default value at the get-site. For example:
String email = properties.getOrDefault("emailFrom", "hello#abc.com");
This is clean and concise, but does mean you need to specify the default everywhere you access the property.
If that won't work for you (i.e. you'll be reading values from the properties object more than once) you can use the built-in support for default values -notice the constructor that takes a default Properties object. This lets you construct a Properties object containing your defaults, and then when you load the user's properties file it will fall back on the defaults if the user doesn't specify a value.
private static final Properties DEFAULTS = new Properties();
static {
DEFAULTS.setProperty("emailFrom", "hello#abc.com");
}
public Properties getProperties() {
Properties props = new Properties(DEFAULTS);
props.load(...);
return props;
}
Just notice that this isn't identical to how Map's constructor works - the defaults are left as a separate map, and only .getProperty() also queries the defaults; the methods defined in Map like .get() don't. One of the many reasons it was a terrible decision for Properties to extend Hashtable, but c'est la vie...
These options work, but they're both error-prone since a) Properties is mutable and b) only some of its public methods fall back the default instance. I prefer to never expose Properties objects directly, and instead create a wrapper class with type-safe methods that expose the values my application will care about. This is a little more typing, but it's much safer to work with. It would look something like this:
public class ApplicationSettings {
private final Properties properties = new Properties();
public ApplicationSettings() {
properties.load(...);
}
public String emailFrom() {
// simple methods are concise, and encode the default right inline
return properties.getOrDefault("emailFrom", "hello#abc.com");
}
public int getMaxTestSeconds() {
// You can do more complex validation if you want, too
String value = properties.get("maxTestInSec");
if (value == null) {
return 30;
}
int maxTestSeconds = Integer.parseInt(value);
if (maxTestSeconds <= 0) {
// could instead log a warning and return the default if you want
throw new IllegalStateException(
"maxTestInSec must be positive - was " + maxTestSeconds);
}
return maxTestSeconds;
}
}
If you need you can also expose setters that similarly validate the values before adding them to the Properties object, though by default making everything read-only is generally a good practice.
In case of a property is commented out, the return will be null, so simply do a null check.
if (prop.getProperty("name")==null)
In case of a value is not filled, check whether its equal to empty space after trim operation.
if (prop.getProperty("name").trim().equals(""))
You can try cashing the properties into static map and process on that map before its being used actually.
private Map<String, String> rawProps = new HashMap<String, String>;
public static Map<String, String> actualProps = new HashMap<String, String>;
static {
checkMapForNullAndReport();
}
private static void checkMapForNullAndReport() {
// Null logic and Reporting logic
// Empty rawProps and populate the actualProps
}
Something like this would work for you i believe.

How do you parse arguments for a java program?

I'm making a Selenium WebDriver java program. I have 25 application and 4 environments. I need to be able to pass something like -app app1 app2 app3 ... appn -env env1 env2 envn
I need to be able to pass either, neither or both arguments. Right now I have it being able to pass one app and one env in that order but I need to be able to do it in either order and with the either neither or both possibility. Here's what I have so far. With this I can either pass no arguments and runs every app for every environment (which is what I want) or I can pick app1 env1 in that order for that specific test.
public static Application chooseAppTest(String[] args)
{
Application application = null;
switch (Application.valueOf(args[0]))
{
case ACCOUNTINVENTORY:
new AccountInventory(Environment.valueOf(args[1]));
AccountInventory.accountInventoryDatabaseTests(testResults);
break;
if (args.length == 0)
{
LogIn.loginTest(testResults);
DatabaseTest.testResults(testResults);
LinkTest.linkTests(testResults);
}
else
{
// First choose application, then choose environment
Application.chooseAppTest(args);
}
I don't think recursion is needed. You can do something like this:
public static void main (String[] args)
{
List<String> apps = new LinkedList<>();
List<String> envs = new LinkedList<>();
List<String> current = null;
// parse arguments
for (String arg : args)
{
if (arg.equals("-app")) current = apps;
else if (arg.equals("-env")) current = envs;
else if (current != null) // add argument
current.add(arg);
}
// parsing finished
Application.doSomethingWith(apps, envs);
}
It is not necessary or advantagious to use recursion. You can read all the arguments into an array and process them from there. Other than that, I'm not sure how you would proceed. With the arguments arranged in this way, how do you know which environment goes with which application?
As Elliott commented, have you looked at Apache Commons CLI? It's a command line parser.

How to use R model in java to predict with multiple models?

I have this constructor:
public Revaluator(File model,PrintStream ps) {
modelFile=model;
rsession=Rsession.newInstanceTry(ps, null);
rsession.eval("library(e1071)");
rsession.load(modelFile);
}
i want to load a model and predict with it.
the problem that Rsession.newInstanceTry(ps, null); is always the same session, so if i load another model, like:
Revaluator re1=new Revaluator(new File("model1.RData"),System.out);
Revaluator re2=new Revaluator(new File("model2.RData"),System.out);
Both re1 and re2 using the same model, since the var name is model, so only the last one loaded.
the evaluate function:
public REXP evaluate(Object[] arr){
String expression=String.format("predict(model, c(%s))", J2Rarray(arr));
REXP ans=rsession.eval(expression);
return ans;
}
//J2Rarray just creates a string from the array like "1,2,true,'hello',false"
i need to load about 250 predictors, is there a way to get every instance of Rsession as a new separated R Session?
You haven't pasted all of your code in your question, so before trying the (complicated) way below, please rule out the simple causes and make sure that your fields modelFile and rsession are not declared static :-)
If they are not:
It seems that the way R sessions are created is OS dependent.
On Unix it relies on the multi-session ability of R itself, on Windows it starts with Port 6311 and checks if it is still free. If it's not, then the port is incremented and it checks again, if it's free and so on.
Maybe something goes wrong with checking free ports (which OS are you working on?).
You could try to configure the ports manually and explicitly start different local R servers like this:
Logger simpleLogger = new Logger() {
public void println(String string, Level level) {
if (level == Level.WARNING) {
p.print("! ");
} else if (level == Level.ERROR) {
p.print("!! ");
}
p.println(string);
}
public void close() {
p.close();
}
};
RserverConf serverConf = new RserverConf(null, staticPortCounter++, null, null, null);
Rdaemon server = new Rdaemon(serverConf, this);
server.start(null);
rsession = Rsession.newInstanceTry(serverConf);
If that does not work, please show more code of your Revaluator class and give details about which OS you are running on. Also, there should be several log outputs (at least if the log level is configured accordingly). Please paste the logged messages as well.
Maybe it could also help to get the source code of rsession from Google Code and use a debugger to set a breakpoint in Rsession.begin(). Maybe this can help figuring out what goes wrong.

Java: ExceptionInInitializerError caused by NullPointerException when constructing a Locale object

I'm working on localization for a program I've written with a couple other guys. Most of the strings now load in the appropriate language from an ini file. I'm trying to do the same with the format of currency in the program. However, I'm getting a runtime exception as soon as I attempt to launch the application.
I'm using the Locale object as a parameter to a few NumberFormat.getCurrencyInstance()'s, like so:
private static final NumberFormat decf;
static
{
decf = NumberFormat.getCurrencyInstance(Lang.cLocale);
decf.setRoundingMode(RoundingMode.HALF_UP);
}
Lang is the class which contains all the localization stuff. The code the IDE complains about at attempted runtime is public static Locale cLocale = new Locale(GUI.DB_info[19],GUI.DB_info[20]);
GUI is the class the GUI is contained in, and where we decided to construct the DB_info array (which itself just contains information loaded from a remote database in another class). DB_info[19] is the language code (es right now) and DB_info[20] is the country code (US). The array elements are being properly filled-- or were, I can't get far enough into the program to tell right now; but nothing has changed with the code for filling DB_info.
The full exception is as follows:
Exception in thread "main" java.lang.ExceptionInInitializerError
at greetingCard.GUI.<clinit>(GUI.java:118)
Caused by: java.lang.NullPointerException
at java.util.Locale.<init>(Unknown Source)
at java.util.Locale.<init>(Unknown Source)
at greetingCard.Lang.<clinit>(Lang.java:13)
... 1 more
The line in GUI referenced is: static String welcome = Lang.L_WELCOME + ", " + empName;, and Lang.java basically looks like this:
// Set locale for currency display
public static Locale cLocale = new Locale(GUI.DB_info[19],GUI.DB_info[20]); // language, country
// Employee specific strings
public static String L_AMT_REMAIN = "";
public static String L_AMT_TEND = "";
public static String L_APPROVED = "";
public static String L_ARE_YOU_SURE = "";
[...]
public static void Main(String emp_lang)
{
String header = "";
if (emp_lang.equals("ENG"))
{
header = "ENG";
}
else if (emp_lang.equals("SPA"))
{
header = "SPA";
}
else if (emp_lang.equals("FRE"))
{
header = "FRE";
}
else if (emp_lang.equals("GER"))
{
header = "GER";
}
else
{
header = "ENG";
}
try
{
Ini ini = new Ini(new File("C:/lang.ini"));
L_AMT_REMAIN = ini.get(header, "L_AMT_REMAIN");
L_AMT_TEND = ini.get(header, "L_AMT_TEND");
L_APPROVED = ini.get(header, "L_APPROVED");
L_ARE_YOU_SURE = ini.get(header, "L_ARE_YOU_SURE");
[...]
L_WELCOME = ini.get(header, "L_WELCOME");
L_WELCOME2 = ini.get(header, "L_WELCOME2");
L_XACT_CHNG = ini.get(header, "L_XACT_CHNG");
L_YES = ini.get(header, "L_YES");
System.err.println("Employee Language: " + header);
}
catch (InvalidFileFormatException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
} // end public static void main
That's for the majority of the strings to be displayed in different languages. There is another method inside Lang that loads some other strings, independent of the first set. I don't believe it factors into this problem but I can post it if needed.
The order in which these classes/methods get launched is as follows:
GUI.Main calls the Login class, which calls a CreateLogin method. That method calls Clients.main, which gets the DB_info array from GUI passed to it. Clients fills the DB_info array. Lang.other is then called (to get language-specific strings for the login page), and the Login buttons and labels are created. Once a login is successful, the perferred language of the employee logging in (from a DB) is passed to Lang.main to load the other strings (hence the emp_lang being passed in the code above).
Up until I added the code for the Locale object, all of this worked fine. Now I get the ExceptionInInitializerError exception. Anyone know what's going on?
BTW, for loading from the ini file I'm using ini4j. Some forum posts I found while googling suggest this is a problem with that, but I don't see how it relates to the problem with Locale objects. The ini stuff works (worked) fine.
Sounds like you have a cycle in your static initializers, so something is not initialized yet.
GUI calls Lang's static initializer before getting Lang.L_WELCOME. Lang calls GUIs static initializer in line 2. Your exception trace makes it look like GUI calls Langs static initializer for some reason.
In all, cycles like this mean that someone is going to reference a statically initialized object and get null instead of what they expected to get. In this case, I suspect Lang.java, line 2, is passing two null pointers to the Locale constructor.
As Keith notes, you have a static initializer cycle. To help future readers...
To minimize these bugs, initialize (simple) constants (with no or minimal constructors) before (complex) variables, so here String before Locale – less room for cycles to cause problems.
Debugging-wise, NullPointerException on a static field and 2 <clinit> in stack trace, with the earlier class appearing in the failing line, are the clues that this is an uninitialized field caused by a static initializer cycle.

how to get the command line arguments from another class with java

so suppose I have a java package....
it's got the main class with the main method
and then it's got a whole bunch of other classes.....
my question is, is it possible to get the args that was passed into the main method from these other classes that are not part of the main class but in the same package...
No, not portably, there may be some trickery based on the JVM implementation but I've never seen it, and it would be a very bad idea to rely on it even if it existed.
If you want those values elsewhere, the main function needs to make them available somehow.
An easy way to do this (not necessarily the best way) is to simply store away the strings as the first thing in main and provide a means for getting at them:
Scratch2.java:
public class Scratch2 {
// Arguments and accessor for them.
private static String[] savedArgs;
public static String[] getArgs() {
return savedArgs;
}
public static void main(String[] args) {
// Save them away for later.
savedArgs = args;
// Test that other classes can get them.
CmdLineArgs cla = new CmdLineArgs();
cla.printArgs();
}
}
CmdLineArgs.java:
public class CmdLineArgs {
public void printArgs() {
String[] args = Scratch2.getArgs();
System.out.println ("Arg count is [" + args.length + "]");
for (int i = 0; i < args.length; i++) {
System.out.println ("Arg[" + i + "] is [" + args[i] + "]");
}
}
}
And, when run with the arguments a b c, this outputs:
Arg count is [3]
Arg[0] is [a]
Arg[1] is [b]
Arg[2] is [c]
The system-properties on some (?) JRE-implementations provide the system-property "sun.java.command" to get the programm-name and parameters that were used to start the program. Like "myjar.jar param1 param2 ...".
While this value doesn't even belong to the set of properties that are mentioned in the documentation, it is present in both Oracle-JRE v1.8 and OpenJRE v1.8 (tested).
I couldn't find any documentation whether this value is supported by default though (best I could find was the list in the System#getProperties() docs). Any clarification on this would be welcome. Handle with care!!!
If you don't care about OS portability, read /proc/self/cmdline or the equivalent for your OS.
See http://en.wikipedia.org/wiki/Procfs
As paxdiablo said, your main class would have to store these parameters and then either distribute or make available to needed ones. Often a good idea would be to let another class do the parsing of these parameters, and provide an object of this class instead of the raw command line to whoever needs it.
I'm kind of a newb at this, but you should be able to store the string[] args to a private instance variable, then make an accessor method for it.
E.g.,
public class test {
private String[] myArgs = new String[10];
public static void main(String[] args) {
myArgs = args;
}
public String[] getArgs() {
return myArgs;
}
}
Not sure if it will work, but that's the idea.

Categories