java - Set final fields with reflection in Constructor - java

I'm trying to make a multi-language app with messages inside multiple *.properties files. I've started working on something like this:
public Language(#NotNull Map<String, String> info) {
Validate.notNull(info, "Language information cannot be null");
this.PLUGIN_PREFIX = info.get("PLUGIN_PREFIX");
this.ARGUMENT_CODE = info.get("ARGUMENT_CODE");
// etc...
}
Now, there's a lot of messages, and I don't feel like typing the same thing each time (plus there could me typos which could be an issue...).
The first solution I thought of was to loop through all of the fields that are like that (in caps, final, not static, etc.) and then use reflection to use the field name as a key to set it as the value. Obviously the compiler won't let me because it thinks that the final field hasn't been initialized.
Something like this:
public Language(#NotNull Map<String, String> info) {
Validate.notNull(info, "Language information cannot be null");
Field[] fields = /* TODO get fields */ new Field[0];
for (Field f : fields) f.set(f.getName(), info.get(f.getName()));
}
Is there a way this can work? Or is there a better solution?
Edit: Quick naming conventions question, should these final "constants" be in upper case?

Usually, you don't store text messages directly in constants, but rather just message keys. Then you use these keys to fetch the actual text messages in the map.
You can use a map directly, but in Java, there is ResourceBundle. A ResourceBundle can be loaded directly from a .properties file.
my-bundle_en.properties:
my.message=Hello, world!
my-bundle_fr.properties:
my.message=Bonjour tout le monde!
my-bundle_de.properties:
my.message=Hallo Welt!
Something.java:
public static final MY_MESSAGE = "my.message";
ResourceBundle bundle = ResourceBundle.getBundle("my-bundle");
String text = bundle.getMessage(MY_MESSAGE);
System.out.println(text);

Related

Get Original Field Name on GraphQL

I'm using https://github.com/leangen/graphql-spqr with spring-boot java application. I can reach to alias name easily but how can I reach to original fieldName?
class Food {
#GraphQLQuery(name = "aliasNameX", description = "A food's name")
private String originalName;
...
}
....
#GraphQLQuery(name = "foods") // READ ALL
#Override
public List<Food> getFoods(#GraphQLEnvironment ResolutionEnvironment env) {
DataFetchingFieldSelectionSet selectionSet = env.dataFetchingEnvironment.getSelectionSet();
List<SelectedField> fields = selectionSet.getFields();
for (SelectedField f: fields)
{
System.out.println(f.getName());
}
return foodRepository.findAll();
}
When I run this code, Output looks like with alias fields: "aliasNameX", ..., but I need original name like "originalName". Is there a way to do it?
Solved, according to:
https://github.com/leangen/graphql-spqr/issues/381
Posting my original answer here as well.
You want the underlying field names, but from a level above. Still possible, but ugly :(
for (SelectedField selectedField : env.dataFetchingEnvironment.getSelectionSet().getImmediateFields()) {
Optional<Operation> operation = Directives.getMappedOperation(selectedField.getFieldDefinition());
String javaName = operation.map(op -> ((Member) op.getTypedElement().getElement()).getName()).orElse(null);
}
Be very careful though. If there's more than one Java element exposed per GraphQL field, getTypedElement().getElement() will explode. So to be sure you'd have to call getTypedElement().getElements() (plural) instead and decide what to do. ClassUtils#getPropertyMembers might also be useful, or the ClassUtils.findXXX family of methods.
You'd basically have to do this:
List<AnnotatedElement> elements = getTypedElement().getElements();
//Look for a field and use its name
Optional<String> field = Utils.extractInstances(elements, Field.class).findFirst().map(Field::getName);
//Look for a getter and find its associated field name
Optional<String> getter = Utils.extractInstances(elements, Method.class).findFirst().map(ClassUtils::getFieldNameFromGetter);
This API might have to change in future, as SDL-based tools are proliferating, so complex directives like the ones SPQR is using are causing problems...

Best way to implement a link between N constants

I couldn't find a better title (feel free to edit it if you find a better one), but the use case is the following. I have two lists of constants. One of those contains the constants I use in my application, the other contains the different constants that are sent to me via a CSV file (along with data).
To give a rough exemple : in the CSV file, there is a field called "id of the client". In my application, I want to use a field called "clientId". So I basically need to create a static link between the two constants, so that I can easily switch from one to the other depending on what I need to achieve.
I've thought about creating a static Map(String, String) of values, but I figured there might be better solutions.
Thanks !
EDIT : changed title to "N" constants instead of 2, because Hashmap doesn't seem to be an option any longer in that case.
you can use the double bracket innitializer idiom to keep map initialization close to the map declaration, so it would be not so "ugly" eg:
static Map<String, String> someMap = new HashMap<String, String>() {{
put("one", "two");
put("three", "four");
}};
Beware that without the static modifier each anonymous class (there is one created in this example) holds a refernce to the enclosing object and if you'll give a reference to this map to some other class it will prevent the enclosing class from being garbage collect.
Fortunatelly, there is a hope for us with java update, in java 9 there will be very handy Map.of() to help us do it more safely.
The best way to separate the mapping from your application code is to use a properties file where in which you define your mapping.
For example, you could have a csv-mapping.properties in the root of your resources and load them with the following code:
final Properties properties = new Properties();
properties.load( this.getClass().getResourceAsStream( "/csv-mapping.properties" ) );
This will work just like a Map, with the added separation of code from configuration.
There are many methods that you can use to easily solve these types of problem.
One way is to use a Properties file, or file containing the key value pair.
Here is the code for Properties.
import java.util.ResourceBundle;
public class ReadingPropertiesFile {
public static void main(String[] args) {
ResourceBundle messages;
messages = ResourceBundle.getBundle("msg");
System.out.println(messages.getString("ID"));
}
}
msg.properties file contains values::
ID = ClientID.
PRODUCT_ID = prod_ID
The output of the program is ClientID.
You can also read from a simple text file. Or you could use the map as you are using. But I would suggest you to use the properties file.
One good option would be to use an enum to create such mappings beetween multiple constants to a single common sense value, eg:
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public enum MappingEnum {
CLIENT_ID("clientId", "id of the client", "clientId", "IdOfTheClient"),
CLIENT_NAME("clientName", "name of the client", "clientName");
private Set<String> aliases;
private String commonSenseName;
private MappingEnum(String commonSenseName, String... aliases) {
this.commonSenseName = commonSenseName;
this.aliases = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(aliases)));
}
public static MappingEnum fromAlias(String alias) {
for (MappingEnum mappingEnum : values()) {
if (mappingEnum.getAliases().contains(alias)) {
return mappingEnum;
}
}
throw new RuntimeException("No MappingEnum for mapping: " + alias);
}
public String getCommonSenseName() {
return commonSenseName;
}
}
and then you can use it like:
String columnName = "id of the client";
String targetFieldName = MappingEnum.fromAlias(columnName).getCommonSenseName();

Property file based conditional patterns in java

I have a property file (a.txt) which has the values (Example values given below) like below
test1=10
test2=20
test33=34
test34=35
By reading this file, I need to produce an output like below
value = 35_20_34_10
which means => I have a pattern like test34_test2_test33_test1
Note, If the 'test33' has any value other than 34 then I need to produce the value like below
value = 35_20_10
which means => I have a pattern like test34_test2_test1
Now my problem is, every time when the customer is making the change in the logic, I am making the change in the code. So what I expect is, I want to keep the logic (pattern) in another property file so I will be sending the two inputs to the util (one input is the property file (A.txt) another input will be the 'pattern.txt'),
My util has to be compare the A.txt and the business logic 'pattern.txt' and produce the output like
value = 35_20_34_10 (or)
value = 35_20_10
If there an example for such pattern based logic as I expect?
Any predefined util / java class does this?
Any help would be Great.
thanks,
Harry
First of all, svasa's answer makes a lot of sense, but covers different level of
abstraction. I recommend you read his answer too, that pattern should
be useful.
You may wanna look at Apache Velocity and FreeMarker libraries to see how they structure their API.
Those are template engines - they usually have some abstraction of pattern or format, and abstraction of variable/value binding (or namespace, or source). You can render a template by binding it with a binding/namespace, which yields the result.
For example, you may wanna have a pattern "<a> + <b>", and binding that looks like a map: {a: "1", b: "2"}. By binding that binding to that pattern you'll get "1 + 2", when interpreting <...> as variables.
You basically load the pattern from your pattern.txt, then load your data file A.txt (for example, by treating it as properties and using Properties class) and construct binding based on these properties. You'll get your output and possibility to customize the pattern all the time.
You may call the sequences like test34_test2_test33_test1 as a pattern, let me call them as constraints when building something.
To me this problem best fits into a
builder pattern.
When building the value you want, you tell the builder that these are my constraints(pattern) and these are my original properties like below:
new MyPropertiesBuilder().setConstraints(constraints).setProperties(original).buildValue();
Details:
Set some constraints in a separate file where you specify the order of the properties and their values like :
test34=desiredvalue-could-be-empty
test2=desiredvalue-could-be-empty
test33=34
test1=desiredvalue-could-be-empty
The builder goes over the constraints in the order specified, but get the values from the original properties and build the desired string.
One way to achieve your requirement through builder pattern is to define classes like below :
Interface:
public interface IMyPropertiesBuilder
{
public void setConstraints( Properties properties );
public void setProperties( Properties properties );
public String buildValue();
}
Builder
public class MyPropertiesBuilder implements IMyPropertiesBuilder
{
private Properties constraints;
private Properties original;
#Override
public void setConstraints( Properties constraints )
{
this.constraints = constraints;
}
#Override
public String buildValue()
{
StringBuilder value = new StringBuilder();
Iterator it = constraints.keySet().iterator();
while ( it.hasNext() )
{
String key = (String) it.next();
if ( original.containsKey( key ) && constraints.getProperty( key ) != null && original.getProperty( key ).equals( constraints.getProperty( key ) ) )
{
value.append( original.getProperty( key ) );
value.append( "_" );
}
}
return value.toString();
}
#Override
public void setProperties( Properties properties )
{
this.original = properties;
}
}
User
public class MyPropertiesBuilderUser
{
private Properties original = new Properties().load(new FileInputStream("original.properties"));;
private Properties constraints = new Properties().load(new FileInputStream("constraints.properties"));
public String getValue()
{
String value = new MyPropertiesBuilder().setConstraints(constraints).setProperties(original).buildValue();
}
}

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.

Prepend beginning to object name and set text in java

I'm trying to pick up java quickly and looking for a way to set the text of a number of labels in my java app.
What I have is a java app that starts\stops\checks status of windows services. I have a method, which is passed an array of these service names and each of these services has a corresponding label that contains it's status. For example, DummyService1 is contained in the array and there is a label called txt_DummyService1. My method (short version) does the following
public static void Checker(String Array[])
{
//check status of DummyService1
"txt_"+DummyService.Text = "started";
}
I realize that this isn't the way that you do this, but could anybody help me out with the best way to do this?
There's no way to generate a "variable" name from a String in this manner. Yes, you might use reflection, but that already rasies questions about the quality of the design.
Instead. Place each label into a Map keyed by it's name.
private Map<String, JLabel> labelLookup = new HashMap<>(25); // Instance variable.
In you constructor (or where ever you build your UI), add each label to the Map.
/* Other UI code */
labelLookup.put("DummyService1", txt_DummyService1);
Now, when you need to do you changes, simply look up the label by it's name
// You had better have a VERY good reason for making this static...
public void checker(String services[])
{
for (String service : services) {
JLabel label = labelLookup.get(service);
if (label != null) {
label.setText("Started");
}
}
}
For example...
Actually I was looking for something more like the following
public static void Checker()
{
try
{
Object Instance = getClass().getDeclaredField("txt_DummyService").get(this);
Method m = Instance.getClass().getMethod("setText",String.class);
m.invoke(Instance,"started");
}
catch(Exception e)
{
//exception handling
}
}
You cannot manipulate variable names at runtime since these are only available to the compiler. One solution to your problem is to keep a Map<String, JLabel> (assuming you are using JLabel and not some other component) to associate a name with each JLabel. I'm sure there are several other possible solutions depending on the exact design of your code.

Categories