Strategy to share static Strings across my app - java

I have lots of values in properties files, which are read in my app to setup values (DB connections, email servers, etc.).
db.properties:
db.user=admin
db.pwd=secret1234
Now in my DatabaseService class, I have something like this:
private static final String DB_USER = "db.user";
private static final String DB_PWD = "db.pwd";
private Properties dbProps = new Properties();
// read db.properties values into dbProps
String user = dbProps.getProperty(DB_USER);
Then in my DatabaseServiceTest class, I have repeated code:
private static final String DB_USER = "db.user";
private static final String DB_PWD = "db.pwd";
private Properties dbProps = new Properties();
// read db.properties values into dbProps
String user = dbProps.getProperty(DB_USER);
So I have repeated code. So instead I have put the static String values into a StaticVars class that hosts all of the Strings so the DatabaseService and DatabaseServiceTest now look like this (I could also put the Properties in the utility class, but there are scores of this example, so I haven't so far):
private Properties dbProps = new Properties();
// read db.properties values into dbProps
String user = dbProps.getProperty(StaticVars.DB_USER);
Is there a better way to share the static Strings across multiple class files? My current StaticVars class has about 150 static String values, and growing. It seems like I am going down the wrong path.
Thanks,
Sean

I think your general approach - using public static final String members of a public class - is a fine way to share strings across an application.
However don't underestimate the importance of naming. When you come back to this code in 6 months will you remember that the names of your properties are stored in a class called StaticVars? If you are truly only storing property names, then perhaps the class should be called PropertyNames. Now you have bounded the scope of the class and will be less likely mix in strings for error messages or regular expressions or whatever. (Those should go into different classes with meaningful names to help you remember what kind of values they store.)
Taking this a step further, since these are property names, they are likely to be used in getProperty calls. So why not rename the class PropertyUtils or ConfigUtils, and have matching static methods which use the property names. Then you can add default property values if certain properties are optional.
public static final String DB_HOST = "db.host";
public static final String DB_USER = "db.user";
public static final String DB_PWD = "db.pwd";
public static String getDbHost(Properties props)
{
return props.getProperty(DB_HOST, "localhost");
}
public static String getDbUser(Properties props)
{
return props.getProperty(DB_USER, "admin");
}
public static String getDbPwd(Properties props)
{
return props.getProperty(DB_PWD);
}

Related

Hash maps to link several domains into one report - Java Eclipse

I am new to Java and am using Java Eclipse, so please be kind! I hope I'm going to pose this question correctly so it makes sense.
I have four domains - each domain is pulling data from four different servers, hence the need to have them separate. But now I need to create a report that links all the data from the four domains into one report. Someone suggested using hashmaps, which I haven't used before. My four domains each have two fields that can be used as a key - CostCenter and Serial. The data being pulled is from machines all over the country. I need all the data for each machine in one report.
This is all being added to an existing project that creates a webpage with tabs for the user to click on for various tables and get data specific to a location, or to create a report for each page for all machines/locations. I just need to add a new link for the user to click on that will create this spreadsheet for them.
I've already created the domains (DAO, DAOImpl, DTO, and so on) and then I was going to create the combined report in my MainService.java. Here are the domains (lists) as declared in my MainService:
public List<Volume> getVolumeReport();
public List<BadFmPumps> getBadFmPumpsReport();
public List<BadCorobPumps> getBadCorobPumpsReport();
public List<McService> getMcServiceReport();
And here is data being pulled from the databases for each of them (domains):
public class Volume {
private String costCenter;
private String DAD;
private String division;
private String model;
private String serial;
private String numDispensers;
private String colorantSys;
private String CCEGals2017;
private String BACGals2017;
private String CCEGals2018;
private String BACGals2018;
private String DNR2017;
private String DNR2018;
private String DNR2019;
public class BadFmPumps {
private String costCenter;
private String model;
private String serial;
private String badFmPumps;
private String over10;
private String under10;
public class BadCorobPumps {
private String costCenter;
private String model;
private String serial;
private String badPumpCount;
public class McService {
private String costCenter;
private String model;
private String serial;
private String crChargeTotals;
private String emgCalls;
So I need to pull this data into one report wherever CostCenter + Serial matches. How do I declare the hashmaps for each object and how do I declare the key?
EDIT ----
I think I have something close here with
public List<Volume> getVolumeReport();
Map<String, Volume> VolumeMap = new HashMap<String, Volume>();
for (Volume dispenser : VolumeList)
{
String volumeKey = new StringBuilder().append(Volume.getCostCenter()).append(Volume.getSerial()).toString();
VolumeMap.put(volumeKey, dispenser);
}
Is this correct? I am getting one syntax error - the Map declaration
Map<String, Volume> VolumeMap = new HashMap<String, Volume>();
is giving me the error
Syntax error on token ";", { expected after this token
Is there something I need to change there?
There are some unusual things in your code. My guess is that you came from C# you are not using proper naming conventions see it here: https://www.oracle.com/technetwork/java/codeconventions-135099.html
You defined your method wrong, the error is not in the Map but the method definition
public List<Volume> getVolumeReport(); <-------- this
Should be
public List<Volume> getVolumeReport() {
And then close your method at its end (using }).
And inside your FOR you trying to direct access the Volume methods when you should use the variable you created: dispenser
String volumeKey = new StringBuilder()
.append(Volume.getCostCenter())
.append(Volume.getSerial())
.toString();
Should be
String volumeKey = new StringBuilder()
.append(dispenser.getCostCenter())
.append(dispenser.getSerial())
.toString();

Implementing a class of "constants" initialized at application start not at compile time

I'm working on a Java project that uses a big class of constants like:
public final class Settings {
public static final int PORT_1 = 8888;
public static final int PORT_2 = 8889;
...
}
Now, some of the value of those constants are not available at compile time anymore so I need a way to "initialize" them at application starts (e.g. from the args[]). Once initialized there should be no way to change them. I'm not very skilled in java, how do I do this in an acceptable way?
I thought of using a singleton with something like a "one shot" set method that throws an exception if called more than one time but it seams too hacky...
You can use a static initializer like this:
public final class Settings {
public static final int PORT_1;
public static final int PORT_2;
...
static {
// create the value for PORT_1:
PORT_1 = ...;
// create the value for PORT_2:
PORT_2 = ...;
}
}
The static initializer is executed during class loading. The final keywords on PORT_1 and PORT_2 protects them to be changed afterwards.
Well, using system properties is a way of doing it unless there is a huge amount of constants.
private static final String CONSTANT1 = System.getProperty("my.system.property");
private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));
System properties are passed on the command line when starting the application using the -D flag.
If there are too many variables a static initializer can be used where a property file or similar can be read that holds the properties:
public class Constants {
private static final String CONSTANT1 = System.getProperty("my.system.property");
private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));
private static final String CONSTANT3;
private static final String CONSTANT4;
static {
try {
final Properties props = new Properties();
props.load(
new FileInputStream(
System.getProperty("app.properties.url", "app.properties")));
CONSTANT3 = props.getProperty("my.constant.3");
CONSTANT4 = props.getProperty("my.constant.3");
} catch (IOException e) {
throw new IllegalStateException("Unable to initialize constants", e);
}
}
}
Note that if you are using some external framework such as Spring Framework or similar there is usually a built-in mechanism for this. E.g. - Spring Framework can inject properties from a property file via the #Value annotation.
There is no simple way to do this in Java. One way to simulate this is to use a builder which returns an internal type (so it can write the private fields) but the internal type only has getters.
See this answer: https://stackoverflow.com/a/1953567/34088

Why not use ResourceBundle instead of Properties?

This is an easy question to which I can't find a concluding answer.
I can load string properties (e.g.: a query for a prepared statement) from a config.properties file. Let's say I want to take the database connection to which to connect.
If I want to take this information from the file, I could do just the following in a class:
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("scheduler");
private static final String DRIVER = BUNDLE.getString("bd.driver");
private static final String CONNECTIONURL =BUNDLE.getString("bd.url");
....
But instead I've seen that many people recommend using instead Properties, Then I would have to do the same with something like this (if I want to keep the class static and not have a proper constructor):
static {
prop = new Properties();
try { prop.load(ReportsDB.class.getClassLoader().getResourceAsStream("config.properties"));
} catch (IOException ex) {
Logger.getLogger(ReportsDB.class.getName()).log(Level.SEVERE, null, ex);
throw new RuntimeException(ex);
}
}
private static final String DRIVER = prop.getProperty("bd.driver");
private static final String CONNECTIONURL = prop.getProperty("bd.url");
So, why shouldn’t I use the ResourceBundle instead of Properties when the second one is more verbose?
So, why shouldn’t I use the ResourceBundle instead of Properties when the second one is more verbose?
Because that's not what ResourceBundle is for. The description of the class starts with:
Resource bundles contain locale-specific objects. When your program needs a locale-specific resource, a String for example, your program can load it from the resource bundle that is appropriate for the current user's locale. In this way, you can write program code that is largely independent of the user's locale isolating most, if not all, of the locale-specific information in resource bundles.
Does any of this sound like your use case? I don't think so.
It sounds like the problem is purely the verbosity of loading a properties file: so write a utility method to do that. Then your code can be simply:
private static final Properties CONFIGURATION = PropertyUtil.load("scheduler.properties");
private static final String DRIVER = CONFIGURATION.getString("bd.driver");
private static final String CONNECTIONURL = CONFIGURATION.getString("bd.url");
Admittedly I'm not keen on having static field initializers in an order-dependent way like that... I'd be tempted to encapsulate all of the configuration in a separate class, so you could write:
private static final SchedulerConfiguration CONFIG =
SchedulerConfiguration.load("scheduler.properties");
then use CONFIG.getDriver() etc which could fetch from the properties each time, or use a field, or whatever.
One concrete difference is that ResourceBundle.getBundle("scheduler") will search for the file in the classpath (the src package folder for example). If you call ResourceBundle.getBundle("myfile") on an external file you will get the MissingResourceException.
If you want to use an external file (a file located in the project root for example) you can use the Properties class:
Properties configuration = new Properties();
try (InputStream input = new FileInputStream("configuration.properties")) {
configuration.load(input);
System.out.println("Configuration value: " + configuration.getProperty("key"));
} catch (IOException e) {
e.printStackTrace();
}

How to write a configurable Java variable creation

A new java component that is suppose to send along a list of pre-define variable to external party. User will then able to define which variable (a fixed set of variable) they want to send via a xml property file
Problem is: The pre-define variable is inside a class, and have business logic together with it, which i can't change or tap into (part of external api). eg as follow:
public class DataColumn implement java.io.Serializable{
public static final String _actionID = "ActionID";
public static final String _actionName = "ActionName";
public static final String _actionDesc = "ActionDescription";
public static final DataColumn ActionDesc = new DataColumn (_actionDesc);
public static final DataColumn ActionID = new DataColumn (_actionID);
public static final DataColumn ActionName = new DataColumn (_actionName);
....and about 100 other variale more
protected WebMediaReportColumn(java.lang.String value) {
_value_ = value;
_table_.put(_value_,this);
}
}
user will define in the xml file like following:
<interface>
<fields isRequired="true">
<field>ActionID</field>
<field>ActionName</field>
<field>ActionDescription</field>
</fields>
</interface>
In order to set which variable to send across, the code looks like following:
interface.setColumns(new DataColumn[] {
DataColumn.ActionID, DataColumn.ActionName, DataColumn.ActionDesc
}
);
Question is: how best to write a program that can base on what the user define in a property file, and create the Datacolumn[] Array accordingly? Without using If else block which is too long and hard to maintain.
Write all these statements in a property file.
actionID = ActionID
actionName = ActionName
actionDesc = ActionDescription
/*... and so on */
In your code create an Properties object and load this file into it. Create an String array and populate the values from this properties object.
Properties p = new Properties();
/*...load the file...*/
String []columnData = new String[p.keySet().size()];
int i=0;
for(Object str : p.keySet()) {
columnData[i++]=(String)p.getProperty((String)str);
}
/*now call the method and pass the array*/
interface.setColumns(columnData);

Where to initialize a java Properties object?

I inherited an application which uses a java properties file to define configuration parameters such as database name.
There is a class called MyAppProps that looks like this:
public class MyAppProps {
protected static final String PROP_FILENAME = "myapp.properties";
protected static Properties myAppProps = null;
public static final String DATABASE_NAME = "database_name";
public static final String DATABASE_USER = "database_user";
// etc...
protected static void init() throws MyAppException {
try {
Classloader loader = MyAppException.class.getClassLoader();
InputStream is = loader.getResourceAsStream(PROP_FILENAME);
myAppProps = new Properties();
myAppProps.load(is);
} catch (Exception e) {
threw new MyAppException(e.getMessage());
}
}
protected static String getProperty(String name) throws MyAppException {
if (props==null) {
throw new MyAppException("Properties was not initialized properly.");
}
return props.getProperty(name);
}
}
Other classes which need to get property values contain code such as:
String dbname = MyAppProps.getProperty(MyAppProps.DATABASE_NAME);
Of course, before the first call to MyAppProps.getProperty, MyAppProps needs to be initialized like this:
MyAppProps.init();
I don't like the fact that init() needs to be called. Shouldn't the initialization take place in a static initialization block or in a private constructor?
Besides for that, something else seems wrong with the code, and I can't quite put my finger on it. Are properties instances typically wrapped in a customized class? Is there anything else here that is wrong?
If I make my own wrapper class like this; I always prefer to make strongly typed getters for the values, instead of exposing all the inner workings through the static final variables.
private static final String DATABASE_NAME = "database_name"
private static final String DATABASE_USER = "database_user"
public String getDatabaseName(){
return getProperty(MyAppProps.DATABASE_NAME);
}
public String getDatabaseUser(){
return getProperty(MyAppProps.DATABASE_USER);
}
A static initializer looks like this;
static {
init();
}
This being said, I will readily say that I am no big fan of static initializers.
You may consider looking into dependency injection (DI) frameworks like spring or guice, these will let you inject the appropriate value directly into the places you need to use them, instead of going through the indirection of the additional class. A lot of people find that using these frameworks reduces focus on this kind of plumbing code - but only after you've finished the learning curve of the framework. (DI frameworks are quick to learn but take quite some time to master, so this may be a bigger hammer than you really want)
Reasons to use static initializer:
Can't forget to call it
Reasons to use an init() function:
You can pass parameters to it
Easier to handle errors
I've created property wrappers in the past to good effect. For a class like the example, the important thing to ensure is that the properties are truly global, i.e. a singleton really makes sense. With that in mind a custom property class can have type-safe getters. You can also do cool things like variable expansion in your custom getters, e.g.:
myapp.data.path=${myapp.home}/data
Furthermore, in your initializer, you can take advantage of property file overloading:
Load in "myapp.properties" from the classpath
Load in "myapp.user.properties" from the current directory using the Properties override constructor
Finally, load System.getProperties() as a final override
The "user" properties file doesn't go in version control, which is nice. It avoids the problem of people customizing the properties file and accidentally checking it in with hard-coded paths, etc.
Good times.
You can use either, a static block or a constructor. The only advice I have is to use ResourceBundle, instead. That might better suit your requirement. For more please follow the link below.
Edit:
ResourceBundles vs Properties
The problem with static methods and classes is that you can't override them for test doubles. That makes unit testing much harder. I have all variables declared final and initialized in the constructor. Whatever is needed is passed in as parameters to the constructor (dependency injection). That way you can substitute test doubles for some of the parameters during unit tests.
For example:
public class MyAppProps {
protected static final String PROP_FILENAME = "myapp.properties";
protected Properties props = null;
public String DATABASE_NAME = "database_name";
public String DATABASE_USER = "database_user";
// etc...
public MyAppProps(InputStream is) throws MyAppException {
try {
props = new Properties();
props.load(is);
} catch (Exception e) {
threw new MyAppException(e.getMessage());
}
}
public String getProperty(String name) {
return props.getProperty(name);
}
// Need this function static so
// client objects can load the
// file before an instance of this class is created.
public static String getFileName() {
return PROP_FILENAME;
}
}
Now, call it from production code like this:
String fileName = MyAppProps.getFileName();
Classloader loader = MyAppException.class.getClassLoader();
InputStream is = loader.getResourceAsStream(fileName);
MyAppProps p = new MyAppProps(is);
The dependency injection is when you include the input stream in the constructor parameters. While this is slightly more of a pain than just using the static class / Singleton, things go from impossible to simple when doing unit tests.
For unit testing, it might go something like:
#Test
public void testStuff() {
// Setup
InputStringTestDouble isTD = new InputStreamTestDouble();
MyAppProps instance = new MyAppProps(isTD);
// Exercise
int actualNum = instance.getProperty("foo");
// Verify
int expectedNum = 42;
assertEquals("MyAppProps didn't get the right number!", expectedNum, actualNum);
}
The dependency injection made it really easy to substitute a test double for the input stream. Now, just load whatever stuff you want into the test double before giving it to the MyAppProps constructor. This way you can test how the properties are loaded very easily.

Categories