I am using Spring Message source to load my property file.
String placeDetails = messageSource.getMessage(code,
null, new Locale(locale.toLowerCase()));
sample entry in property file:
BNA=Nashville:Nashville:USA
property file name:
placeDetails_locale.properties
The messagesource.getMessage method returns the assigned value ex If code "BNA"
String "placeDetails" will hold "Nashville:Nashville:USA".If the code not found in the property file it throws "No such message found exception".But I need to handle this situation like If no message found in that scenario I have to set the default value to placeDetails.
Approach I tried:
1.I need to check first If the code is available in that property file then only i need to call get messagesource method.But I am unaware on how to check the value presence through Message source
2.I need to define the default value look up in the catch block.
I tried second but not working as expected.Help me on how to check the availability of code in property file through Message source(first appraoch)
Use below to return a default message in case you don't one. You don't have to handle exception cases.
getMessage(String code, Object[] args, String defaultMessage, Locale locale)
There are two ways you can specify the default values for localized messages.
The preferred one is to provide a placeDetails.properties as well, that works as a catch all in case a given message key isn't found in placeDetails_locale.properties.
The second one (a quick fix) is to use the following overloaded getMessage() method
String placeDetails = messageSource.getMessage(code, null,
"placeDetails", new Locale(locale.toLowerCase()));
Notice, how the third argument specifies the default value in case the key lookup fails.
Unfortunately MessageSource seems not to have a containsMessageKey(String) method or similar and in addition to that a null value as defaultMessage is getting ignored - in such case the messageKey will be returned.
I tried to solve that issue with the following approach:
#Service
#Slf4j
public class MessageService {
#Autowired
private MessageSource messageSource;
private static final String messageKeyNotFound = "messageKey-not-found";
public String getMessage(MessageKey messageKey, Locale locale) {
String i18nMessage = messageSource.getMessage(messageKey.name(), null, messageKeyNotFound, locale);
if (i18nMessage.equals(messageKeyNotFound)) {
log.error("missing translation for messageKey '"+messageKey.name()+"' and locale '" + locale.getLanguage()+"'");
return messageKey.name();
}
return i18nMessage;
}
}
While MessageKey is an Enum containing valid keys.
public enum MessageKey {
title,
name,
age;
}
With this solution the enum's and property's messageKeys need to have identical names
title=Title
name=Name
age=Age
You should write a unit test which iterates through all available keys and expected locales to catch any typos. But to be on the save side you will get error logs in case an unexpected locale comes in.
Related
I have a Spring Boot project in Kotlin which uses a custom locale interceptor to know from which .properties file it load messages.
This works pretty fine. Also I have a custom annotation which is simple and has a default message value, as follow:
#Target(AnnotationTarget.Field)
#Constraint(validatedBy = [MyCustomValidator::class])
annotation class MyAnnotation(
val message: String = "{javax.validation.constraints.MyAnnotation.message}",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = []
)
class MyCustomValidator : ConstraintValidator<MyAnnotation, String> {
override fun isValid(value: String, context: ConstraintValidatorContext) {
return true //Just to make it easy
}
}
The locale properties files contains the key MyAnnotation.value=This field is required and shows as the exception message.
The problem is when I want to add more validations and so, custom messages according to each condition. I read that I should disable the default constraint validator and add the messages, but it is not working.
For example, if I want to create a key at locale file as MyAnnotation.conditionOne, it still prints the message from MyAnnotation.value.
//The only thing that changes in MyAnnotation is that message: String = ""
//Assuming that ConditionOne and ConditionTwo has a isValid static method
class MyCustomValidator : ConstraintValidator<MyAnnotation, String> {
override fun isValid(value: String, context: ConstraintValidatorContext): Boolean {
context.disableDefaultConstraintViolation()
return if (!ConditionOne.isValid(value)) {
context
.buildConstraintViolationWithTemplate("{javax.validation.constraints.MyAnnotation.conditionOne}")
.addConstraintViolation()
false
}
else if (!ConditonTwo.isValid(value)) {
context
.buildConstraintViolationWithTemplate("{javax.validation.constraints.MyAnnotation.message}")
.addConstraintViolation()
false
}
else
true
}
}
Is this the right way to set the message?
As I can see on the example above, you added two constraint violation message but in the different if cases. For a getting several checks and violation messages from one validator you should have not placed return after each if cases, instead of this you can create a local boolean variable and set its value from each of if statement cases, and after all make return that variable.
Pay attention to a little thing: it is important for your validator to set a temporary boolean variable correctly, because if once your if was set into false that means ultimate return value should be false. Cuz there is a principle anyMatch(false) or allMatch(true)
It is so common in UI logic that you have a method to checkValid(something). Its primary use is to return a Boolean. But in case of invalid (or false), we may want to provide an additional user message about what is being invalid, after all the checkValid() method might have checked 20 different things. By simply responding with "not valid" to the user is not very helpful.
So is there a built-in Java class or some from Apache commons that does just that? I understand it is so simple to build my own, but my sense tells me this is so common I must missed it somewhere in common packages.
Status codes have long been used to return "Success" (often 0) or "Failure" responses, with failures further identified by distinct non-zero values. The Enum class is a friendlier way of doing this.
enum Status {
SUCCESS("Success"),
BAD_PATH("The directory does not contain the required file"),
HAS_THE_LETTER_C("The directory must not contain the letter C");
private final String message;
private Status(String _message) {
message = _message;
};
#Override
public String toString() {
return message;
}
}
Status status = checkValid(something);
if (status != Status.SUCCESS) {
// Inform user of specific failure reason
}
Apache commons has a Pair class, so you could use a Pair<Boolean, String>. Alternatively, you could just return a String with the validation failure, and treat a null as non-error (i.e., the validation either returns the reason of a failure or null if there's no failure).
I am using Apache Commons Configuration library with PropertiesConfiguration.
My application loads the config file right after its started, like this:
public PropertiesConfiguration loadConfigFile(File configFile) throws ConfigurationNotFoundException {
try {
if (configFile != null && configFile.exists()) {
config.load(configFile);
config.setListDelimiter(';');
config.setAutoSave(true);
config.setReloadingStrategy(new FileChangedReloadingStrategy());
setConfigLoaded(true);
}
else {
throw new ConfigurationNotFoundException("Configuration file not found.");
}
} catch (ConfigurationException e) {
logger.warn(e.getMessage());
setDefaultConfigValues(config);
config.setFile(configFile);
}
return config;
}
My question is, how can I validate the configFile, so I can be sure that no property in that file is missing and later in my code I won't get a NullPointerException when trying to access the properties, e.g.:
PropertiesConfiguration config = loadConfig(configFile);
String rootDir = config.getString("paths.download"); // I want to be sure that this property exists right at the app start
I didn't found anything in the documentation or google, just something about XML validation.
The goal is to provide feedback to the user at program start that the configuration file is corrupted.
There is no build-in mechanism for properties-file?
What is a configuration object supposed to do if you pass in a key to one of its get methods that does not map to an existing property?
the default behavior as implemented in AbstractConfiguration is to return null if the return value is an object type.
For primitive types as return values returning null (or any other special value) is not possible, so in this case a NoSuchElementException is thrown
// This will return null if no property with key "NonExistingProperty" exists
String strValue = config.getString("NonExistingProperty");
// This will throw a NoSuchElementException exception if no property with
// key "NonExistingProperty" exists
long longValue = config.getLong("NonExistingProperty");
For object types like String, BigDecimal, or BigInteger this default behavior can be changed:
If the setThrowExceptionOnMissing() method is called with an argument of true, these methods will behave like their primitive counter parts and also throw an exception if the passed in property key cannot be resolved.
Situation is little tricky for Collection & array types as they will return empty collection or array.
Is there a way to retrieve the value of a var in a .properties file and use is inside the same .properties file?
Insted of this (where I have to write manually the words 'Main Menu' in every line)
mainMenuTitle = Main Menu
NotBlank.menu.title = the field 'Main Menu' can't be null
Size.menu.title = the field 'Main Menu' must contains 10 characters
I want something like this (where I get automatically the value of the var 'mainMenuTitle')
mainMenuTitle = Main Menu
NotBlank.menu.title = the field **mainMenuTitle** can't be null
Size.menu.title = the field **mainMenuTitle** must contains 10 characters
You can get both message separately and then make a replace to inject the title
#Inject
public MessageSource messageSource;
public static String getMessage(String messageKey1, String messageKey12) {
String title = messageSource.getMessage(messageKey1, arguments, locale);
String message = messageSource.getMessage(messageKey2, arguments, locale).replace("**mainMenuTitle**", title);
}
This May be what you want, its a bit old , but may work for your needs.
Enabling constant substitution in Property Values
You can substitute a constant anywhere in the property value, and even have more than one constant within a value, as in the following example:
CONST_1 = shoes and ships
CONST_2 = sealing wax
SomeValue = {CONST_1} and {CONST_2}
In this example, the "SomeValue" property evaluates to "shoes and ships and sealing wax."
I have a Liferay scheduled job which should create new AssetCategories according to some rules. But I always get an AssetCategoryNameException. I have only letters, spaces and parenthesis in my new category's name/title, all of which I used in the existing category names. I also tried with constant string with english letters, with the same result. What could be the cause of this exception?
My code looks like this:
AssetCategoryLocalServiceUtil.addCategory(userId, 0, titleMap, null,
myVocabulary.getVocabularyId(), null, serviceContext);
userId is the id of a random Administrator user since it's a scheduled job and there is no "logged in" user.
titleMap is created with the following code: HashMap<Locale, String> titleMap = new HashMap<Locale, String>(); titleMap.put(myLocale, name);
serviceContext is a new ServiceContext object
And one more thing, how can I use the categoryProperties attribute of this method (after the problem is solved)? It is a String[], but properties are key-value pairs.
Following are invalid characters for AssetCategoryName
public static char[] INVALID_CHARACTERS = new char[] {
CharPool.AMPERSAND, CharPool.APOSTROPHE, CharPool.AT,
CharPool.BACK_SLASH, CharPool.CLOSE_BRACKET, CharPool.CLOSE_CURLY_BRACE,
CharPool.COLON, CharPool.COMMA, CharPool.EQUAL, CharPool.GREATER_THAN,
CharPool.FORWARD_SLASH, CharPool.LESS_THAN, CharPool.NEW_LINE,
CharPool.OPEN_BRACKET, CharPool.OPEN_CURLY_BRACE, CharPool.PERCENT,
CharPool.PIPE, CharPool.PLUS, CharPool.POUND, CharPool.QUESTION,
CharPool.QUOTE, CharPool.RETURN, CharPool.SEMICOLON, CharPool.SLASH,
CharPool.STAR, CharPool.TILDE
};
Make sure your category name wont have any of these characters.
OK, I found the solution. The problem was caused by the locale settings. AssetCategory has both a name and a localized title, and the addAssetCategory method uses the title from the default locale as name. But in my case, the default locale was en_US while I was putting only hu_HU title in my titleMap. So, the title of the default locale was null, and that caused the exception (note: a NullPointerException would be more informative...). So I changed the default locale to my locale, and the code works.