I am reading a property file in Java.
Properties myProp = new Properties();
InputStream in = new FileInputStream(pathOfPropertyFile);
myProp.load(in);
in.close();
The values in the property file have references to Linux shell variables.
For example, an entry in the property file might look like:
DATA_PATH=/data/${PROJECT}/${YEAR}${MONTH}${DAY}
I have to execute a shell script from java and so I have ProcessBuilder instance and also the environment variables (envMap as given below):
List<String> command = new ArrayList<String>();
command.add(actualCommand);
command.add(param1);
command.add(param2);
ProcessBuilder processBuilder = new ProcessBuilder(command);
Map<String, String> envMap = processBuilder.environment();
The envMap has the environment variables I require along with over one hundred (> 100) other environment variables which I do not require.
I want to replace the ${USER},${PROJECT},etc., from the property-value string "/home/${USER}/${PROJECT}/data" with actual values from the shell.
I would consider iterating the Map as the last option(as the Map has between 100 and 200 elements to iterate) as it is not an efficient approach.
Please advise some approach that will fetch the environment variable enclosed by braces from the string, so that I can directly use the get() of the Map and replace. Or, any better approaches are most welcome.
Note: The reference offered ( Replace String values with value in Hash Map that made my question to look duplicate) is not the best fit in my case.
If you are open to using an external library, StrSubstitutor from apache-commons will do exactly what you want:
public static void main(String[] args) {
String input = "DATA_PATH=/data/${PROJECT}/${YEAR}${MONTH}${DAY}";
Map<String, String> env = new HashMap<>();
env.put("PROJECT", "myProject");
env.put("YEAR", "2017");
env.put("MONTH", "7");
env.put("DAY", "5");
env.put("OTHER_VALUE", "someOtherValue");
System.out.println(StrSubstitutor.replace(input, env));
}
Output:
DATA_PATH=/data/myProject/201775
It also has a method to directly replace system properties without the need for an explicit map.
(for a non-external-library approach see vefthym's answer)
I am not sure if it works, but I hope someone can edit to make it work, or at least you get the logic and make it work on your own:
command = command.replaceAll("\\$\\{(.*?)\\}", envMap.get("$1"));
Here, I am assuming that command is a String (not a List) and that all environment variables exist in your Map (otherwise you should check for null and handle this case as you wish).
A bit of an explanation:
this regex is looking for the pattern "${something}" and replaces it with envMap.get("something"). In this example, we use parentheses to mark "something" as a group, which can then be retracted as "$1" (since we have only one group, i.e., only one set of parentheses).
The question mark '?' is the non-greedy operator here meaning to stop at the smallest possible regex match (otherwise it would find a singe match for the first "${" until the last "}".
Related
How do I write this in Java?
//js
const hello = {
foo: "bar",
test: "world",
name: "david"
}
I want have a very long object, then refer it back like hello[test] or hello[foo]
I've heard of hashmaps, but you can only create an empty one and then add elements into it.
I've got a really long list like that in js. How can I copy those into Java? Doing .put() one by one would take forever, and I don't think that's efficient.
And even if someone wrote a script to turn uwu: "owo" into hello.put("uwu", "owo");, it'd be ugly in the code with a big block of hello.put()s.
I also don't want to create a new file for that (it only has around 34 lines) and want to keep it in the code. Also, because I have three more like these with 20-40 keys and values in each of them, I don't want to create three extra files with just 30 lines in them. I also don't want to go into complexity of reading them.
Oh and also, I won't be changing the hashmap btw, just reading data like a constant.
In summary, can I do something like this in Java for long lists without doing .put()?
public HashMap<String, String> hello = new HashMap<String, String>(
"foo": "bar",
"test": "world",
"name": "david",
"uwu": "owo"
);
And refer to them like hello["name"]? I also don't want this thing.
public HashMap<String, String> hello = new HashMap<String, String>();
hello.put("foo", "bar");
hello.put("test", "world");
hello.put("name", "david");
hello.put("uwu", "owo");
//for 25 more lines
public HashMap<String, String> hello2 = new HashMap<String, String>();
hello2.put("stuff", "thing");
//... for around 20 more lines
//repeat for 3 more hashmaps
In modern Java (14 and later) you can use a record:
record Hello(String foo, String test, String world) { }
and create an instance like this:
final Hello hello = new Hello("bar", "world", "david");
You access the values like:
System.out.print(hello.foo());
Using a record has the advantage that your data is statically typed -- you can't mistype a key, or forget to remove usages of a key you've removed from the record.
IN Java 14 and beyond, I would recommand using a record, as explained in the other answer.
It's the safest and also probably the most efficient way.
For Java 9 to 14, you may use Map.of("hello", "world", "foo", "bar");.
But you may not be able to go beyond a certain number of key/value pairs.
For java 8 and below, or if you exceed the number of arguments allowed with Map.of, you don't have other choice than create an empty map and put key/value pairs one by one.
Note however that, performances aren't necessarily going to be worse.
You can of course reimplement your own version of Map.of with variable number of arguments.
Since you need something constant like, you can save those values in files and read from those files. For example save data in file in json format:
{
"foo": "bar",
"test": "world",
"name": "david"
}
Then parse this file to a Map.
public class Main {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(ClassLoader.getSystemResourceAsStream("constants.json"), Map.class);
map.forEach((k, v) -> System.out.println(k + " -> " + v));
}
}
This example uses reading file as project resource and uses ObjectMapper to parse json to Map, but you can use any other tool for the same effect. If the data format is simple enough(string key to string value, no nested arrays, objects and such) you can save it in even simpler format and do the read, parse, add to map manually.
How can I get the input order of parameters in JCommander?
I want to know which parameter was input first.
For example
Myapp --cut -- reverse
Myapp --reverse -- cut
Is this possible in Jcommander?
And if so, how?
One way to achieve your need is instead of define multiple flag.
See Arities (multiple values for parameters)
Then read your command args:
#Parameter(names = "-cmd", variableArity = true)
public List<String> commands= new ArrayList<>();
MyApp -cmd cut reverse -anotherCmd
I'm using the Play Framework (Java) and am not able to figure out how to use environment variables in my configuration file for building the database connection string. While I'm able to use environment variables like this (for user name and password):
default.username = ${?FU_MAIN_DB_USERNAME}
default.password = ${?FU_MAIN_DB_PASSWORD}
I'm not able to make it work in the url string. Perhaps this is a simple case of string processing in Scala that I'm missing, but since I'm working in Java, I could use some help.
So far, I have tried the url string in the following formats and failed:
Tried to add a $ to variable name to perform interpolation:
default.url = "jdbc:postgresql://$${?FU_MAIN_DB_HOST}:$${?FU_MAIN_DB_PORT}/$${?FU_MAIN_DB_NAME}";
But this doesn't substitute. Rather, it picks the string as such.
default.url = "jdbc:postgresql://${?FU_MAIN_DB_HOST}:${?FU_MAIN_DB_PORT}/${?FU_MAIN_DB_NAME}";
This too inserts the '$' and all verbatim. Then I thought maybe something like PHP-style will work
default.url = "jdbc:postgresql://${${?FU_MAIN_DB_HOST}}:${${?FU_MAIN_DB_PORT}}/${${?FU_MAIN_DB_NAME}}";
But no.
I also tried doing stuff like "jdbc:postgresql://".concat(${?FU_MAIN_DB_HOST}) ... but this also inserts '.concat' verbatim.
Finally, I tried concatenation using the '+' operator, but I'm told (by my IDE) that symbols like +: etc. are not allowed in the application.conf file.
How then, in God's name, am I supposed to do that?!
The double quotes turn off interpolation. But you need to do that for the : and the //.
Try
default.url = "jdbc:postgresql://"${?FU_MAIN_DB_HOST}":"${?FU_MAIN_DB_PORT}/${?FU_MAIN_DB_NAME}
Maybe you are better off to set the whole thing in one big environment variable instead.
In java,i have a Map that contains certain values. I want to create a String variable that having the name of the key of the map. How it is possible in java?
Map<String,String> values=new HashMap<String, String>();
values.put("dataSource", "bloomberg");
values.put("dataProvider", "bloomberg");
values.put("observationTime", "close");
this is the map. And i want to make the variables with values :
String dataSource="bloomberg";
String dataProvider="bloomberg";
String observationTime="close";
How it is possible in java?
Java doesn't support dynamic variable names. All the variables have to be present at compile time (this means that only the containers would be made at compile time) and at runtime, values are simply assigned and changed according to the logic of the program.
A few languages support creating variables with dynamic names, but it is not possible in Java
Try this
String dataSourceKey = "dataSource";
String dataSourceValue = "bloomberg";
values.put(dataSourceKey, dataSourceValue);
Now, when reading the values from the map
String dataSource = values.get(dataSourceKey);
Hope this helps
I want to display errors detected in an action class, I use:
errors.add(ActionErrors.GLOBAL_MESSAGE,
new ActionMessage("some_string_in_properties_file"));`
and it works fine. However, I have written some generic error messages, and I would like to reuse them, so I am trying to do this:
errors.add(ActionErrors.GLOBAL_MESSAGE,
new ActionMessage("string1_in_properties_file", "string2_in_properties_file"));
where string1 = <li>{0} is required.</li>.
Then it is displaying string2 is required. It is not replacing string2 with its value.
I even tried
errors.add(ActionErrors.GLOBAL_MESSAGE,
new ActionMessage("string1_in_properties_file",
new ActionMessage("string2_in_properties_file")));
then it is displaying string2[] is required. It is not replacing string2.
I know it can be done by hard-coding the value, but is there any other way?
Since you want to to fetch two key's value from Property file, and put it in global error key,
I would say, retrieve each value separately using
String sValue1 = getResources(request).getMessage(locale, "key1");
String sValue2 = getResources(request).getMessage(locale, "key2");
and then put it in your global error
errors.add(ActionErrors.GLOBAL_MESSAGE,sValue1+"<br/>"+sValue2);
Hope it help....
It's hard to tell you exactly what to do, since O don't know the code behind errors and ActionMessage. But you can, however, use String.format. Your code would look something like this
public class ActionErrors {
public static final String INVALID_INPUT "'%s' is not valid input.";
...
}
and
String input = "Cats";
String message = String.format(ActionErrors.INVALID_INPUT, input);
System.out.println(message);
The above will print
'Cats' is not valid input.
In Struts ActionMessage, you can specify value for your parameters {0}, {1}, {2}, {3} specified in your properties file, as follows:
errors.add(ActionErrors.GLOBAL_MESSAGE,
new ActionMessage("some_string_in_properties_file", "value1"));
Alternately:
errors.add(ActionErrors.GLOBAL_MESSAGE,
new ActionMessage("some_string_in_properties_file", "value1", "value2", "value3"));
value1..value3 can be of any type (as Struts expects an Object).
so your property:
string1 = <li>{0} is required.</li>
Will be replaced to:
<li>value1 is required.</li>
(If you specify your key as string1).
Let's say you have a properties file that defines some keys for messages, like so:
string1: <li>{0} is required.</li>
string2: Username
The ActionMessage class has a number of constructors that take a varying number of arguments. The first is a string representing the key that refers to a message - in your case, the key is string1 which corresponds to the message <li>{0} is required.</li>; with the {0} being a placeholder for some dynamic content.
The remaining possible arguments are Objects that represent the actual values you want to replace those placeholders. If you do new ActionMessage("string1", "string2") you're passing in the literal value string2, and you'll end up with output of <li>string2 is required.</li>.
What you need to do is replace "string2" with a method call that will get the value that corresponds to the key string2. This is where my knowledge of the problem runs out, though, so you'll need to do some research on this part for yourself.