Wrap string keys to make distinction between external and internal keys - java

We have two kinds of IDs, internal and externalA. Currently records have both internal and externalA IDs, and in future there might be externalB types of IDs so some records could have internal and externalB IDs.
We currently represent all IDs as Strings. It can lead to bugs, where a method expects internal, but externalA key was actually passed.
How do we prevent this type of error? Would wrapping String keys into InternalID, ExternalAID, ExternalBID classes and use those wrappers everywhere work for us?
I am concerned about memory footprint, which could especially happen if we are talking about hundreds of millions of keys, and maybe other stuff that can go wrong.

I think you need a discriminator for all of the string keys. for example:
String internalKey= "I1000201";
String externalAKey= "A1000201";
String externalBKey= "B1000201";
Then you can prevent the error by checking the actual key type by the first char, for example:
char type = key.charAt(0);
if(type != 'I') throw new IllegalArgumentException("Invalid key");
// go on
You also can create your own Key class from String keys if all of the keys take a distinct discriminator.
Key internal = Key.from("I1000201"); //internal key
Key external = Key.from("A1000201"); //external key A
Key.from("X1000201");
// ^--- throws IllegalArgumentException for invalid key type
public class Key {
private final String key;
private final Visibility visibility;
private static final BitSet externals = new BitSet();
static {
// register your own external key here
externals.set('A');
externals.set('B');
}
// v--- make the Key constructor private.
private Key(String key, Visibility visibility) {
this.key = key;
this.visibility = visibility;
}
public static Key from(String key) {
return new Key(key, visibilityOf(key));
}
private static Visibility visibilityOf(String key) {
char type = key.charAt(0);
return type == 'I' ? Visibility.INTERNAL
: externals.get(type) ? Visibility.EXTERNAL
: failsOnInvalidKey(key);
}
private static Visibility failsOnInvalidKey(String key) {
throw new IllegalArgumentException("Invalid Key: \"" + key + "\"");
}
public char type() {
return key.charAt(0);
}
public String value() {
return key.substring(1);
}
public boolean isExternal() {
return visibility == Visibility.EXTERNAL;
}
public String toString() {
return key;
}
// preserve it maybe will introduce additional behavior in future
private enum Visibility {
EXTERNAL,
INTERNAL
}
}

Related

Resolve environment variables of property file Java ResourceBundle

Our application is using java8 and spring. We are working to moving to kubernetes. For that reason, I want to use environment variables in the properties file like as follow and declare the -
conf.dir.path = ${APPLICATION_CONF_PATH}
database.name = ${APPLICATION_DB_SCHEMA}
save.file.path = ${COMMON_SAVE_PATH}${APPLICATION_SAVE_PATH}
# And many more keys
But right now the values are not resolved/expanded by environment variable.
Application initialization of property is as below -
public enum ApplicationResource {
CONF_DIR_PATH("conf.dir.path"),
DB_NAME("database.name")
FILE_SAVE_PATH("save.file.path"),
// And many more keys
private final String value;
ApplicationResource(String value) {
this.value = value;
}
private static final String BUNDLE_NAME = "ApplicationResource";
private static Properties props;
static {
try {
Properties defaults = new Properties();
initEnvironment(defaults, BUNDLE_NAME);
props = new Properties(defaults);
} catch (Throwable t) {
t.printStackTrace();
}
}
private static void initEnvironment(Properties props, String bundleName) throws Throwable {
ResourceBundle rb = ResourceBundle.getBundle(bundleName);
Enumeration<?> enu = rb.getKeys();
String key = null;
String value = null;
while (enu.hasMoreElements()) {
key = (String) enu.nextElement();
value = rb.getString(key);
props.setProperty(key, value);
}
}
public String getString() {
return props.getProperty(value);
}
public int getInt() throws NumberFormatException {
String str = getString();
if (str == null || str.length() == 0) {
return 0;
} else {
return Integer.parseInt(str);
}
}
}
getString is used extensively. Right now when getString is called, it returns the literal string from the properties file. Is there any way to properly resolve environment variables without impacting the codebase?
Edit: By [without impacting the codebase], I meant only changing/editing code in the above enum/class file and the change being transparent in other areas.
The simplest variant based on the Regex engine would be:
private static final Pattern VARIABLE = Pattern.compile("\\$\\{(.*?)\\}");
public String getString() {
return VARIABLE.matcher(props.getProperty(value))
.replaceAll(mr -> Matcher.quoteReplacement(System.getenv(mr.group(1))));
}
This replaces all occurrences of ${VAR} with the result of looking up System.getenv("VAR"). If the string contains no variable references, the original string is returned. It does, however, not handle absent variables. If you want to handle them (in a different way than failing with a runtime exception), you have to add the policy to the function.
E.g. the following code keeps variable references in their original form if the variable has not been found:
public String getString() {
return VARIABLE.matcher(props.getProperty(value))
.replaceAll(mr -> {
String envVal = System.getenv(mr.group(1));
return Matcher.quoteReplacement(envVal != null? envVal: mr.group());
});
}
replaceAll(Function<MatchResult, String>) requires Java 9 or newer. For previous versions, you’d have to implement such a replacement loop yourself. E.g.
public String getString() {
String string = props.getProperty(value);
Matcher m = VARIABLE.matcher(string);
if(!m.find()) return string;
StringBuilder sb = new StringBuilder();
int last = 0;
do {
String replacement = System.getenv(m.group(1));
if(replacement != null) {
sb.append(string, last, m.start()).append(replacement);
last = m.end();
}
} while(m.find());
return sb.append(string, last, string.length()).toString();
}
This variant does not use appendReplacement/appendTail which is normally used to build such loops, for two reasons.
First, it provides more control over how the replacement is inserted, i.e. by inserting it literally via append(replacement) we don’t need Matcher.quoteReplacement(…).
Second, we can use StringBuilder instead of StringBuffer which might also be more efficient. The Java 9 solution uses StringBuilder under the hood, as support for it has been added to appendReplacement/appendTail in this version too. But for previous versions, StringBuilder can only be used when implementing the logic manually.
Note that unlike the replaceAll variant, the case of absent variables can be handled simpler and more efficient with a manual replacement loop, as we can simply skip them.
You said you don’t want to change the initialization code, but I still recommend bringing it into a more idiomatic form, i.e.
private static void initEnvironment(Properties props, String bundleName) {
ResourceBundle rb = ResourceBundle.getBundle(bundleName);
for(Enumeration<String> enu = rb.getKeys(); enu.hasMoreElements(); ) {
String key = enu.nextElement();
String value = rb.getString(key);
props.setProperty(key, value);
}
}
In the end, it’s still doing the same. But iteration loops should be using for, to keep initialization expression, loop condition and fetching the next element as close as possible. Further, there is no reason to use Enumeration<?> with a type cast when you can use Enumeration<String> in the first place. And don’t declare variables outside the necessary scope. And there’s no reason to pre-initialize them with null.
Spring support environment variable or system variable or application.property file
if you able to use kubernates configmap its better choice.
How to set environment variable dynamically in spring test

Compare String in ENUM

I want to implement storing of enabled or disabled features into database row. When some String value is received from them the network I would like to compare it into ENUM.
ENUM:
public enum TerminalConfigurationFeatureBitString {
Authorize("authorize", 0), // index 0 in bit string
Authorize3d("authorize3d", 1), // index 1 in bit String
Sale("sale", 2), // index 2 in bit String
Sale3d("sale3d", 3), // index 3 in bit String
}
Map<TerminalConfigurationFeatureBitString, Boolean> featureMaps =
config.initFromDatabaseValue(optsFromDatabase);
featureMaps.get(transaction.transactionType);
The best way is to use featureMaps.get(TerminalConfigurationFeatureBitString.Sale);
But I don't know the incoming string what would be.
Now I get warning Unlikely argument type String for get(Object) on a Map<TerminalConfigurationFeatureBitString,Boolean>
Is there any other way to make a query into the ENUM without knowing the key?
In cases like these, I often find myself adding a static method getByX which does a lookup based upon a property of the enum:
public enum BitString {
//...
public static Optional<BitString> getByTransactionType(String transactionType)
{
return Arrays.stream(values())
.filter(x -> x.transactionType.equals(transactionType))
.findFirst();
}
}
Usage:
enum TransactionStatus
{
ENABLED, NOT_ENABLED, NOT_SUPPORTED
}
TransactionStatus status = BitString.getBygetByTransactionType(transaction.transactionType)
.map(bitString -> featureMaps.get(bitString))
.map(enabled -> enabled ? TransactionStatus.ENABLED : TransactionStatus.NOT_ENABLED)
.orElse(TransactionStatus.NOT_SUPPORTED);
Similar to #Michael's answer, you can just generate a static lookup map inside your enum which maps an enums transaction type to the actual enum:
private static final Map<String, TerminalConfigurationFeatureBitString> TRANSACTION_TYPE_TO_ENUM =
Arrays.stream(values()).collect(Collectors.toMap(
TerminalConfigurationFeatureBitString::getTransactionType,
Function.identity()
);
And then have a lookup method, also inside the enum:
public static TerminalConfigurationFeatureBitString getByTransactionType(String transactionType) {
TerminalConfigurationFeatureBitString bitString = TRANSACTION_TYPE_TO_ENUM.get(transactionType);
if(bitString == null) throw new NoSuchElementException(transactionType);
return bitString;
}
This in a way more performant than the mentioned answer, because the Map is created the first time the enum is loaded (So when it is the first time referenced). And thus the iteration happens only once. Also Maps have a rather fast lookup time so you could say that getting an enum this way works O(1) (when ignoring the initial computation time of O(n))
You can extend your enum with extra static method which will try to convert given String on enum item:
enum TerminalConfigurationFeatureBitString {
Authorize("authorize", 0), // index 0 in bit string
Authorize3d("authorize3d", 1), // index 1 in bit String
Sale("sale", 2), // index 2 in bit String
Sale3d("sale3d", 3); // index 3 in bit String
private final String value;
private final int index;
TerminalConfigurationFeatureBitString(String value, int index) {
this.value = value;
this.index = index;
}
public String getValue() {
return value;
}
public int getIndex() {
return index;
}
public static Optional<TerminalConfigurationFeatureBitString> fromValue(String value) {
for (TerminalConfigurationFeatureBitString item : values()) {
if (item.value.equals(value)) {
return Optional.of(item);
}
}
return Optional.empty();
}
}
In case option is not found, return Optional.empty(). If feature is not present it means String representation does not represent any feature. Usage:
public void test() {
EnumMap<TerminalConfigurationFeatureBitString, Boolean> featureMaps = new EnumMap<>(
TerminalConfigurationFeatureBitString.class);
Optional<TerminalConfigurationFeatureBitString> feature = TerminalConfigurationFeatureBitString.fromValue("authorize");
if (!feature.isPresent()) {
System.out.println("Feature is not foudn!");
} else {
Boolean authorize = featureMaps.get(feature.get());
if (authorize != null && authorize) {
System.out.println("Feature is enabled!");
} else {
System.out.println("Feature is disabled!");
}
}
}

Most efficient way to convert Enum values into comma seperated String

I have a java class in which I store an Enum.(shown at the bottom of this question) In this enum, I have a method named toCommaSeperatedString() who returns a comma separated String of the enums values. I am using a StringBuilder after reading some information on performance in this question here.
Is the way I am converting this enum's values into a commaSeperatedString the most efficient way of doing so, and if so, what would be the most efficient way to remove the extra comma at the last char of the String?
For example, my method returns 123, 456, however I would prefer 123, 456. If I wanted to return PROPERTY1, PROPERTY2 I could easily use Apache Commons library StringUtils.join(), however, I need to get one level lower by calling the getValue method when I am iterating through the String array.
public class TypeEnum {
public enum validTypes {
PROPERTY1("123"),
PROPERTY2("456");
private String value;
validTypes(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static boolean contains(String type) {
for (validTypes msgType : validTypes.values()) {
if (msgType.value.equals(type)) {
return true;
}
}
return false;
}
public static String toCommaSeperatedString() {
StringBuilder commaSeperatedValidMsgTypes = new StringBuilder();
for(validTypes msgType : validTypes.values()) {
commaSeperatedValidMsgTypes.append(msgType.getValue() + ", ");
}
return commaSeperatedValidMsgTypes.toString();
}
}
}
I wouldn't worry much about efficiency. It's simple enough to do this that it will be fast, provided you don't do it in a crazy way. If this is the most significant performance bottleneck in your code, I would be amazed.
I'd do it something like this:
return Arrays.stream(TypeEnum.values())
.map(t -> t.value)
.collect(Collectors.joining(','));
Cache it if you want; but that's probably not going to make a huge difference.
A common pattern for the trailing comma problem I see is something like
String[] values = {"A", "B", "C"};
boolean is_first = true;
StringBuilder commaSeperatedValidMsgTypes = new StringBuilder();
for(String value : values){
if(is_first){
is_first = false;
}
else{
commaSeperatedValidMsgTypes.append(',');
}
commaSeperatedValidMsgTypes.append(value);
}
System.out.println(commaSeperatedValidMsgTypes.toString());
which results in
A,B,C
Combining this with the answers about using a static block to initialize a static final field will probably give the best performance.
The most efficient code is code that doesn't run. This answer can't ever change, so run that code as you have it once when creating the enums. Take the hit once, return the calculated answer every other time somebody asks for it. The savings in doing that would be far greater in the long term over worrying about how specifically to construct the string, so use whatever is clearest to you (write code for humans to read).
For example:
public enum ValidTypes {
PROPERTY1("123"),
PROPERTY2("345");
private final static String asString = calculateString();
private final String value;
private static String calculateString() {
return // Do your work here.
}
ValidTypes(final String value) {
this.value = value;
}
public static String toCommaSeparatedString() {
return asString;
}
}
If you have to call this static method thousand and thousand of times on a short period, you may worry about performance and you should first check that this has a performance cost.
The JVM performs at runtime many optimizations.
So finally you could write more complex code without added value.
Anyway, the actual thing that you should do is storing the String returned by toCommaSeperatedString and returned the same instance.
Enum are constant values. So caching them is not a problem.
You could use a static initializer that values a static String field.
About the , character, just remove it after the loop.
public enum validTypes {
PROPERTY1("123"), PROPERTY2("456");
private static String valueSeparatedByComma;
static {
StringBuilder commaSeperatedValidMsgTypes = new StringBuilder();
for (validTypes msgType : validTypes.values()) {
commaSeperatedValidMsgTypes.append(msgType.getValue());
commaSeperatedValidMsgTypes.append(",");
}
commaSeperatedValidMsgTypes.deleteCharAt
(commaSeperatedValidMsgTypes.length()-1);
valueSeparatedByComma = commaSeperatedValidMsgTypes.toString();
}
public static String getvalueSeparatedByComma() {
return valueSeparatedByComma;
}
I usually add a static method on the enum class itself:
public enum Animal {
CAT, DOG, LION;
public static String possibleValues() {
return Arrays.stream(Animal.values())
.map(Enum::toString)
.collect(Collectors.joining(","));
}
}
So I can use it like String possibleValues = Animal.possibleValues();

Check if a String equals the name of an Enum constant

I would like to check if a given String equals any Enum constant names in my Enum class. Here is an example:
public enum Relation {
APPLE("an apple"),
BANANA("a banana");
private String value;
private Relation(String s) {
this.value = s;
}
public String getValue() {
return this.value;
}
}
My String would be:
String test = "a banana";
I want to check is the String equals any of the Enum constant names, i.e. "an apple" or "a banana":
if (test.equals(....)) {
System.out.println("You ordered a banana.");
}
So far, the examples I found all apply to checking if a String equals an Enum constant. But I want to check if the String equals any of the constant's names as defined in the parenthesis.
for (Relation relation : Relation.values()) {
if (relation.getValue().equals(string)) {
return relation;
}
}
return null;
You could use a map/ or set to store the enum values, which you would then be able to check for matching values.
This would only be useful if you wanted to do this repeatedly, and reuse the map or set. You would use the map if you wanted to get the related enum.
Map<String, Relation> map = new HashMap<>();
for (Relation relation : Relation.values()) {
map.put(relation.getValue(), relation);
}
if(map.containsKey("a banana")){
System.out.println("You ordered a banana.");
}
or as a set:
Set<String> set = new HashSet<>();
for (Relation relation : Relation.values()) {
set.add(relation.getValue());
}
if(set.contains("a banana")){
System.out.println("You ordered a banana.");
}

Runtime populated enum-like class

I have many objects using few classes (means elements visual categorization like in html+css). Classes are not known at compile-time and they are used in conditions many times.
To improve performance I've got one solution:
public class ElementClass {
private static final Map<String, ElementClass> classes = new HashMap<>();
public final String name;
public final String lowerName;
public ElementClass(String name, String lowerName) {
this.name = name;
this.lowerName = lowerName;
}
public static ElementClass get(String name) {
String lower = name.toLowerCase();
ElementClass c = classes.get(lower);
if (c == null) {
c = new ElementClass(name, lower);
classes.put(lower, c);
}
return c;
}
}
The method get is used very less than comparison of ElementClass variables. It is in parsing configurations and for some static variables. I'm not sure if this is the best way to go, because I'm Java beginner.
The examples usage of ElementClass:
// contains element styles based on it's class
Map<ElementClass,ElementStyle> styles;
void exampleFunction() {
ElementClass c = ElementClass.get("special");
for( Element e : elements ) {
if( e.cls == c ) doSomethingSpecial();
}
}
This would be a textbook implementation of a cache. If there aren't many ElementClasses and if your program is single-threaded, this will be enough.
I don't see the need to keep the lowercase name inside the ElementClass. It is enough to use it as the map key. I also assume there's more to the ElementClass in your project since now it just contains a name.
Update
After clarification it became obvious that you do indeed only intend to use the String name. In such a case it would be much better to make each Element just contain its lowercase name, but interned:
public Element(String name) {
this.name = name.toLowerCase().intern();
}
Then you can compare element.name == "special" and be guaranteed to match any names that are equal to "special".

Categories