Create AssetCategory in Lifeary scheduled job - java

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.

Related

JavaFX complex string binding

I'm new to JavaFX and was wondering if the Bindings API allowed an easier way to achieve the following. Consider a model that contains a database that may be null (because the database loads asynchronously) and a view that displays a label status reflecting the state of the database. If it is null it should say something like "Loading..." and if it isn't it should display how many items are in the database. It also would be great if the status could reflect the size of the database as it grows or shrinks.
So far, I understand that I could bind an integer property (size of the database) to the text property of the label by using a converter. This is fine, but I want the label to display more than the number. A localized string like "Loaded {0} items" precisely. And let's not forget that the database may still be null.
This is the solution I have in place
#Override
public void initialize(URL url, ResourceBundle bundle) {
// Initialize label with default value
status();
model.databaseProperty().addListener((obs, old, neu) -> {
// Update label when database is no longer null
status();
// Update label when size of database changes
neu.sizeProperty().addListener(x -> status());
});
}
public void status() {
if (model.database() == null) {
status.setText(bundle.getString("status.loading"));
} else {
String text = bundle.getString("status.ready");
int size = model.database().size();
text = new MessageFormat(text).format(size);
status.setText(text);
}
}
It works, but is there a way to do it with a chain of bindings, or at least part of it? I've seen how powerful (and lenghty) boolean bindings can be but I'm not sure something as flexible is possible with string bindings.
You can use Bindings.when, which is essentially a dynamic if/then binding:*
status.textProperty().bind(
Bindings.when(model.databaseProperty().isNull())
.then(bundle.getString("status.loading"))
.otherwise(
Bindings.selectInteger(model.databaseProperty(), "size").asString(
bundle.getString("status.ready")))
);
However, the above assumes bundle.getString("status.ready") returns a java.util.Formatter string, not a MessageFormat string. In other words, it would need to be "Loaded %,d items" rather than "Loaded {0,number,integer} items".
Bindings doesn’t have built-in support for MessageFormat, but if you really want to stick with MessageFormat (which is a legitimate requirement, as there are things MessageFormat can do which Formatter cannot), you can create a custom binding with Bindings.createStringBinding:
MessageFormat statusFormat = new MessageFormat(bundle.getString("status.ready"));
status.textProperty().bind(
Bindings.when(model.databaseProperty().isNull())
.then(bundle.getString("status.loading"))
.otherwise(
Bindings.createStringBinding(
() -> statusFormat.format(new Object[] { model.getDatabase().getSize() }),
model.databaseProperty(),
Bindings.selectInteger(model.databaseProperty(), "size")))
);
* Actually, it’s more like the ternary ?…: operator.

Spring data query with max limit and condition

I want to get the item with the highest year and has a particular personal name. I'm trying this:
Foo findTopByOrderByYearDesc();
This work great, the problem is when I add a new param to filter results
Foo findTopByOrderByYearDescAndPersonName(#Param("person.name") final String name);
But I get this error:
No property andPersonName found for type Foo!
I try this too but I get de same error:
Foo findTopByOrderByYearDescByPersonName(#Param("person.name") final String name);
You should use the following:
Foo findTopByPersonNameOrderByYearDesc(#Param("person.name") final String name);
The first 'by' keyqord works as a delimiter see here

How to get a string from .properties file and use it in the same .properties file

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."

Handle no such messageexception in Message source Spring

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.

Can you make a TextField<BigDecimal> accept both , and . as decimal separator?

In a Wicket app, I have a decimal number text field:
TextField<BigDecimal> f =
new TextField<BigDecimal>("f", new PropertyModel<BigDecimal>(model, "share"));
I want it to always accept both . (dot) and , (comma) as decimal separator (regardless of browser's locale settings).
For showing the value, session's locale is used [which in our case is forced to be "fi" (-> comma)], but here I'm interested in what the field accepts as input.
My question is, do I have to change the field to TextField<String>, and convert to domain object's type (BigDecimal) manually? Or is there some way to use TextField<BigDecimal> (which allows e.g. making use of Wicket's MinimumValidator or RangeValidator), and still have it accept both decimal separators?
Thanks to #bert's comment, and the Wicket in Action book, I found an approach that works. In the Application class specify a custom converter for BigDecimals:
#Override
protected IConverterLocator newConverterLocator() {
ConverterLocator converterLocator = new ConverterLocator();
converterLocator.set(BigDecimal.class, new CustomBigDecimalConverter());
return converterLocator;
}
And in the custom converter, convertToObject needs to be overridden. NB: this is sufficient for our needs; think about your requirements and adapt as needed!
public class CustomBigDecimalConverter extends BigDecimalConverter {
#Override
public BigDecimal convertToObject(String value, Locale locale) {
// NB: this isn't universal & your mileage problably varies!
// (Specifically, this breaks if '.' is used as thousands separator)
if ("fi".equals(locale.getLanguage())) {
value = value.replace('.', ',');
}
return super.convertToObject(value, locale);
}
}
Edit: Offtopic, but I want to document this too. We needed our app to support a scale of 4 decimal places, and our custom BigDecimal converter nicely solves that problem too.
#Override
public String convertToString(Object value, Locale locale) {
NumberFormat fmt = getNumberFormat(locale);
fmt.setMaximumFractionDigits(4); // By default this is 3.
return fmt.format(value);
}
After this customisation, a decimal number like 2.0005 will be shown as 2.0005 instead of 2.

Categories