Custom Annotation-driven Formatting Spring MVC - java

I am migrating a java EE application to spring boot and i got stuck at a converting problem.
Now whether its good or not i stored my currencies as Long (its german euro).
I wrote a custom jsf converter that does something like that:
Long -> String
22 -> 00,22
3310 -> 33,10
String -> Long
3 -> 3
22,11 -> 2211
Now Spring MVC was one reason to move away from JSF.
I would like to make use of 303 Beanvalidation, with Spring MVC (#Valid #ModelAttribute, BindingResult which works fine for #Pattern e.g)
Now i cant use #NumberFormat(style=Style.Currency), which would do what I want, if I have not stored my currency as long.
I wrote a custom Formatter and registered it to FormatterRegistry
public class LongCurrencyFormatter implements Formatter<Long>{
#Getter
private static final long serialVersionUID = 1L;
#Override
public String print(Long arg0, Locale arg1) {
//logic removed for shorter post
}
#Override
public Long parse(String arg0, Locale arg1) throws ParseException {
//logic removed for shorter post
}
}
to this point everthing is working, but now every long is converted. What I think is right.
So after some research I looked into 6.6.2 Annotation-driven Formatting
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
I created as in the documentation an AnnotationFormatterFactory
public class LongCurrencyFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory<LongCurrency> {
#Override
public Set<Class<?>> getFieldTypes() {
Set<Class<?>> setTypes = new HashSet<Class<?>>();
setTypes.add(Long.class);
return setTypes;
}
#Override
public Parser<?> getParser(LongCurrency annotation, Class<?> fieldType) {
return new LongCurrencyFormatter();
}
#Override
public Printer<?> getPrinter(LongCurrency annotation, Class<?> fieldType) {
return new LongCurrencyFormatter();
}
}
My annotation:
public #interface LongCurrency {
}
My Bean:
public class Costunit {
//other attributes
#LongCurrency
private long value;
}
sadly it is not working :
Failed to convert property value of type java.lang.String to required type long for property value; nested exception is java.lang.NumberFormatException: For input string: "22,00"
Sorry for the long post, any idea what i did wrong ? Or any better Solution to bind a formatter to only one controller? A Databasemirgration should be the very least option.
Thank you!
EDIT1: full Formatter code (works but could be better of course)
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;
import java.util.regex.Pattern;
import lombok.Getter;
import org.springframework.format.Formatter;
public class LongCurrencyFormatter implements Formatter<Long>{
#Getter
private static final long serialVersionUID = 1L;
#Override
public String print(Long arg0, Locale arg1) {
String returnValue = arg0.toString();
boolean minusChar = returnValue.startsWith("-");
returnValue = returnValue.replace("-", "");
if (returnValue.length() > 2) {
String tempStr = returnValue.substring(0, returnValue.length()-2);
Long val = Long.parseLong(tempStr);
DecimalFormat df = new DecimalFormat();
df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.GERMAN));
String output = df.format(val) + "," +
returnValue.substring(returnValue.length()-2);
returnValue = output;
} else {
if(returnValue.length() == 1) {
returnValue = "0,0"+returnValue;
} else {
returnValue = "0,"+returnValue;
}
}
if(minusChar) {
returnValue = "-" + returnValue;
}
return returnValue;
}
#Override
public Long parse(String arg0, Locale arg1) throws ParseException {
Long returnLong = null;
// 1Test :only one - in front, only digits and "." and one "," , and
// only 2 digits behind ","
// if "," only 2 not 1 digit behind
if (!isValidateLongCurrency(arg0)) {
returnLong = 0L;
} else {
String valueFiltered = arg0.replace(".", "");
// 2: add 2 00 if no ",":
if (!valueFiltered.contains(",")) {
valueFiltered += "00";
}
else {
//E,C or E,CC
String[] splittedValue = valueFiltered.split(",");
if(splittedValue[splittedValue.length-1].length() == 1) {
valueFiltered = valueFiltered + 0;
}
valueFiltered = valueFiltered.replace(",", "");
}
try {
returnLong = new Long(valueFiltered);
} catch (NumberFormatException numEx) {
}
}
return returnLong;
}
private boolean isValidateLongCurrency(String value) {
boolean returnValue = true;
String valueFiltered = value.replace(".", "");
//Euro
String regEx = "^-?[1-9][0-9]*(,[0-9][0-9]?)?$|^-?[0-9](,[0-9][0-9]?)?$|^$";
returnValue = Pattern.matches( regEx, valueFiltered ) ;
return returnValue;
}
}
EDIT 2, now its works
Changes made:
import java.lang.annotation.*;
#Target(value={ElementType.METHOD,ElementType.FIELD,ElementType.PARAMETER})
#Retention(value=RetentionPolicy.RUNTIME)
public #interface LongCurrency {
}
#Override
public void addFormatters(FormatterRegistry registry) {
super.addFormatters(registry);
registry.addFormatterForFieldAnnotation(new
LongCurrencyFormatAnnotationFormatterFactory());
}
Thanks to M. Deinum

For starters your annotation isn't there anymore. You need to make sure it is retained at runtime, by default annotations are removed. For this add the #Retention meta annotation on your annotation. You probably also want to add the #Target annotation to specify on which types it can be set.
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface LongCurrency {}
Next make sure that you have registered your LongCurrencyFormatAnnotationFormatterFactory properly. If you don't register it it will not be used.
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldAnnotation(new LongCurrencyFormatAnnotationFormatterFactory());
}
Both changes should make that your formatter is going to be called/used.

Related

Can univocity execute validators in OR way insted of AND?

I would like to know if there is any way to execute the validators in an OR operator way insted of AND. Currently if the first fails, he doesn't run the other two, however I need him to run them all to return to the user all the respective fixes that he should apply to the file.
I have something like:
#Validate(nullable = false, validators = { NumericValidator.class, LengthValidator.class, DataValidator.class })
I don't create a single validation since I would like to create general validations and just call the specific depending on the field.
Thanks in advance.
I solved building a class like
import java.util.HashSet;
import java.util.Set;
import com.univocity.parsers.conversions.Validator;
import co.com.app.constantes.Constants;
public class CustomValidator implements Validator<Object> {
protected Set<Validator<Object>> validators = new HashSet<>();
public CustomValidator() {
super();
}
#Override
public String validate(Object value) {
Set<String> errors = new HashSet<>();
validators.forEach(validator -> {
String error = validator.validate(value);
if (error != null) {
errors.add(error);
}
});
if (errors.isEmpty()) {
return null;
}
return String.join(Constants.COMMA, errors);
}
}
Then I create a Validator like
public class FieldValidator extends CustomValidator {
public FieldValidator() {
validators.add(new NumericValidator());
validators.add(new LengthValidator(1, 11));
}
}
And in the DTO I used something like
#Parsed(index = 0)
#NullString(nulls = { Constants.NULL_VALUE, Constants.EMPTY_VALUE })
#Validate(nullable = false, validators = { FieldValidator.class })
private Long field;

How to pass custom data type to cucumber-jvm stepdef in latest 4.x version

I have recently upgraded to latest 4.x version of cucumber-jvm in my project in order to leverage parallel execution feature of cucumber. But I am facing this issue now with respect to having custom data type as parameter. Earlier we had an interface called Transformer which we can implement for custom data types, now in the latest version I've found TypeRegistryConfigurer interface which needs to be implemented. But it is not recognising the step as I would've expected. Details as follows:
Gherkin Step:
Given user gets random(3,true,true) parameter
StepDefinition:
#Given("user gets {random} parameter")
public void paramTest(RandomString randomString) {
System.out.println(randomString.string);
}
RandomString class:
public class RandomString {
public String string;
public RandomString(String string) {
Matcher m = Pattern.compile("random\\((.?)\\)").matcher(string);
String t = "";
while (m.find()) {
t = m.group(1);
}
boolean isAlpha = true, isNum = true;
if (t.length() > 0) {
String[] placeholders = t.split(",");
if (placeholders.length == 3) {
int count = Integer.parseInt(placeholders[0]);
isAlpha = Boolean.valueOf(placeholders[1]);
isNum = Boolean.valueOf(placeholders[2]);
this.string = string.replaceAll("random(.*)", RandomStringUtils.random(count, isAlpha, isNum));
}
}
this.string = string.replaceAll("random(.*)", RandomStringUtils.random(3, isAlpha, isNum));
}
}
TypeRegistryImpl:
public class TypeRegistryConfiguration implements TypeRegistryConfigurer {
#Override
public Locale locale() {
return Locale.ENGLISH;
}
#Override
public void configureTypeRegistry(TypeRegistry typeRegistry) {
typeRegistry.defineParameterType(new ParameterType<>(
"random",
"random([0-9],true|false,true|false)",
RandomString.class,
RandomString::new)
);
}
}
Your string random(3,true,true) does not match the pattern used in:
typeRegistry.defineParameterType(new ParameterType<>(
"random",
"random([0-9],true|false,true|false)",
RandomString.class,
RandomString::new)
);
You can verify this by creating the pattern and testing it:
import java.util.regex.Pattern;
class Scratch {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("random([0-9],true|false,true|false)");
// prints out false
System.out.println(pattern.matcher("random(3,true,true)").matches());
}
}
You have also not used a matching pattern in RandomString.
I've found the solution after trial and hit and going through some examples from some Unit Tests in cucumber-jvm project.
Modified StepDef:
#Given("user gets {random} parameter")
public void paramTest(String randomString) {
System.out.println(randomString.string);
}
TypeRegistryConfigurer Implementation:
import cucumber.api.TypeRegistry;
import cucumber.api.TypeRegistryConfigurer;
import io.cucumber.cucumberexpressions.CaptureGroupTransformer;
import io.cucumber.cucumberexpressions.ParameterType;
import org.apache.commons.lang3.RandomStringUtils;
import java.util.Locale;
public class TypeRegistryConfiguration implements TypeRegistryConfigurer {
#Override
public Locale locale() {
return Locale.ENGLISH;
}
#Override
public void configureTypeRegistry(TypeRegistry typeRegistry) {
typeRegistry.defineParameterType(new ParameterType<>(
"random",
"random\\(([0-9]+),(true|false),(true|false)\\)",
String.class,
new CaptureGroupTransformer<>() {
#Override
public String transform(String[] args) {
return RandomStringUtils.random(Integer.parseInt(args[0]), Boolean.valueOf(args[1]), Boolean.valueOf(args[2]));
}
})
);
}
}

How to read a Thymeleaf template from DB in Thymeleaf 3.0.5?

We're upgrading from Thymeleaf 2.1 to 3.0.5. Our current set up (before upgrading) has many thymeleaf templates defined and stored in a database table.
When we attempt to upgrade to 3.x our 2.1 code no longer works...ok fine but we can't find any good examples on how to do basically the same thing with Thymeleaf 3.0.5. Has anyone implemented this?
Even a decent example of how to implement org.thymeleaf.templateresolver.StringTemplateResolver would probably push us in the right direction...but we can't find anything on that either.
This is what we used in 2.1:
public class ThymeleafTemplateResolver extends TemplateResolver {
private final static String PREFIX = "";
public ThymeleafTemplateResolver() {
setResourceResolver(new DbResourceResolver());
setResolvablePatterns(Sets.newHashSet(PREFIX + "*"));
}
#Override
protected String computeResourceName(TemplateProcessingParameters params) {
String templateName = params.getTemplateName();
return templateName.substring(PREFIX.length());
}
private class DbResourceResolver implements IResourceResolver {
#Override
public InputStream getResourceAsStream(TemplateProcessingParameters params, String template) {
ThymeleafTemplateDao thymeleaftemplateDao = ApplicationContextProvider.getApplicationContext().getBean(ThymeleafTemplateDao.class);
ThymeleafTemplate thymeleafTemplate = thymeleaftemplateDao.findByTemplate(template);
if (thymeleafTemplate != null) {
return new ByteArrayInputStream(thymeleafTemplate.getContent().getBytes());
}
return null;
}
#Override
public String getName() {
return "dbResourceResolver";
}
}
}
Any help is appreciated...
Through mostly trial and error I was able to piece this together. Posting it here to help the next person looking for something similar.
This is made easier in the newer version of Thymeleaf. All one needs to do now is to extend StringTemplateResolver.
import java.util.Map;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import org.thymeleaf.templateresource.ITemplateResource;
import com.google.common.collect.Sets;
public class ThymeleafDatabaseResourceResolver extends StringTemplateResolver {
private final static String PREFIX = "";
#Autowired ThymeleafTemplateDao thymeleaftemplateDao;
public ThymeleafDatabaseResourceResolver() {
setResolvablePatterns(Sets.newHashSet(PREFIX + "*"));
}
#Override
protected ITemplateResource computeTemplateResource(IEngineConfiguration configuration, String ownerTemplate, String template, Map<String, Object> templateResolutionAttributes) {
// ThymeleafTemplate is our internal object that contains the content.
// You should change this to match you're set up.
ThymeleafTemplateDao thymeleaftemplateDao = ApplicationContextProvider.getApplicationContext().getBean(ThymeleafTemplateDao.class);
ThymeleafTemplate thymeleafTemplate = thymeleaftemplateDao.findByTemplateName(template);
if (thymeleafTemplate != null) {
return super.computeTemplateResource(configuration, ownerTemplate, thymeleafTemplate.getContent(), templateResolutionAttributes);
}
return null;
}
}

Xpages: How to ensure my cacheBean only loads once

Still struggling with properly making a cacheBean. I think I want the bean to be a singleton, from what I have read. Will only need
one instance of it. Use it to get often used keywords and so on.
http://blog.defrog.nl/2013/02/prefered-way-for-referencing-beans-from.html
I used this pattern to make my CacheBean (and used a utility method).
If I make this a managedBean by putting it into Faces-config, then I can easily get the value of models
<xp:text escape="true" id="computedField1"
value="#{CacheBean.models}"></xp:text>
The JSF takes care of instantiating the bean for me.
But I don't want it to reload the same values (like models) over and over. I thought that to get that to happen I needed to make
a POJO and grab the currentInstance of the bean, as in the url.
However, when I made this change (taking the bean out of the faces-config file, I cannot seem to get a handle on the properties.
This won't even compile:
<xp:text escape="true" id="computedField1"
value="#{Cache.getCurrentInstance().models}">
</xp:text>
What am I doing wrong?
================================
package com.scoular.cache;
import java.io.Serializable;
import org.openntf.domino.xsp.XspOpenLogUtil;
import com.scoular.Utils;
public class CacheBean implements Serializable {
private static final long serialVersionUID = -2665922853615670023L;
public static final String BEAN_NAME = "CacheBean";
private String pcDataDBpath;
private Vector<Object> models = new Vector<Object>();
public CacheBean() {
initConfigData();
}
private void initConfigData() {
try {
loadModels();
loadDBPaths();
} catch (Exception e) {
XspOpenLogUtil.logError(e);
}
}
// Getters and Setters
public static CacheBean getInstance(String beanName) {
return (CacheBean) Utils.getVariableValue(beanName);
}
public static CacheBean getInstance() {
return getInstance(BEAN_NAME);
}
public String getPcDataDBpath() {
return pcDataDBpath;
}
public void setPcDataDBpath(String pcDataDBpath) {
this.pcDataDBpath = pcDataDBpath;
}
public void loadDBPaths() {
Session session = Factory.getSession();
Database tmpDB = session.getCurrentDatabase();
pcAppDBpath = (tmpDB.getServer() + "!!" + "scoApps\\PC\\PCApp.nsf");
pcDataDBpath = (tmpDB.getServer() + "!!" + "scoApps\\PC\\PCData.nsf");
compDirDBpath = (tmpDB.getServer() + "!!" + "compdir.nsf");
}
public void loadModels() {
try {
Session session = Factory.getSession();
Database tmpDB = session.getCurrentDatabase();
Database PCDataDB = session.getDatabase(tmpDB.getServer(), "scoApps\\PC\\PCData.nsf");
ViewNavigator vn = PCDataDB.getView("dbLookupModels").createViewNav();
ViewEntry entry = vn.getFirst();
while (entry != null) {
Vector<Object> thisCat = entry.getColumnValues();
if (entry.isCategory()) {
String thisCatString = thisCat.elementAt(0).toString();
models.addElement(thisCatString);
}
entry = vn.getNextCategory();
}
} catch (Exception e) {
XspOpenLogUtil.logError(e);
}
}
p
ackage com.scoular;
import javax.faces.context.FacesContext;
public class Utils {
public static Object getVariableValue(String varName) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getVariableResolver().resolveVariable(context, varName);
}
}
When the bean has the right scope you can access the bean directly if is created.
private static final String BEAN_NAME = "CacheBean";
//access to the bean
public static CacheBean get() {
return (CacheBean) JSFUtil.resolveVariable(BEAN_NAME);
}
//in my JSFUtil class I have the method
public static Object resolveVariable(String variable) {
return FacesContext.getCurrentInstance().getApplication().getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(), variable);
}
so in a Java Class you can call
CacheBean.get().models
in EL you can use
CacheBean.models
I can tell you why it's not compiling at least.
value="#{Cache.getCurrentInstance().models}"
That's EL. So there should not be a get or a (). You want
value="#{Cache.currentInstance.models}"
And check your var name as I thought you were using CacheBean and not Cache.

For a large validation task is chain of responsibility pattern a good bet?

I need to build a process which will validate a record against ~200 validation rules. A record can be one of ~10 types. There is some segmentation from validation rules to record types but there exists a lot of overlap which prevents me from cleanly binning the validation rules.
During my design I'm considering a chain of responsibility pattern for all of the validation rules. Is this a good idea or is there a better design pattern?
Validation is frequently a Composite pattern. When you break it down, you want to seperate the what you want to from the how you want to do it, you get:
If foo is valid
then do something.
Here we have the abstraction is valid -- Caveat: This code was lifted from currrent, similar examples so you may find missing symbology and such. But this is so you get the picture. In addition, the
Result
Object contains messaging about the failure as well as a simple status (true/false).
This allow you the option of just asking "did it pass?" vs. "If it failed, tell me why"
QuickCollection
and
QuickMap
Are convenience classes for taking any class and quickly turning them into those respected types by merely assigning to a delegate. For this example it means your composite validator is already a collection and can be iterated, for example.
You had a secondary problem in your question: "cleanly binding" as in, "Type A" -> rules{a,b,c}" and "Type B" -> rules{c,e,z}"
This is easily managed with a Map. Not entirely a Command pattern but close
Map<Type,Validator> typeValidators = new HashMap<>();
Setup the validator for each type then create a mapping between types. This is really best done as bean config if you're using Java but Definitely use dependency injection
public interface Validator<T>{
public Result validate(T value);
public static interface Result {
public static final Result OK = new Result() {
#Override
public String getMessage() {
return "OK";
}
#Override
public String toString() {
return "OK";
}
#Override
public boolean isOk() {
return true;
}
};
public boolean isOk();
public String getMessage();
}
}
Now some simple implementations to show the point:
public class MinLengthValidator implements Validator<String> {
private final SimpleResult FAILED;
private Integer minLength;
public MinLengthValidator() {
this(8);
}
public MinLengthValidator(Integer minLength) {
this.minLength = minLength;
FAILED = new SimpleResult("Password must be at least "+minLength+" characters",false);
}
#Override
public Result validate(String newPassword) {
return newPassword.length() >= minLength ? Result.OK : FAILED;
}
#Override
public String toString() {
return this.getClass().getSimpleName();
}
}
Here is another we will combine with
public class NotCurrentValidator implements Validator<String> {
#Autowired
#Qualifier("userPasswordEncoder")
private PasswordEncoder encoder;
private static final SimpleResult FAILED = new SimpleResult("Password cannot be your current password",false);
#Override
public Result validate(String newPassword) {
boolean passed = !encoder.matches(newPassword,user.getPassword());
return (passed ? Result.OK : FAILED);
}
#Override
public String toString() {
return this.getClass().getSimpleName();
}
}
Now here is a composite:
public class CompositePasswordRule extends QuickCollection<Validator> implements Validator<String> {
public CompositeValidator(Collection<Validator> rules) {
super.delegate = rules;
}
public CompositeValidator(Validator<?>... rules) {
super.delegate = Arrays.asList(rules);
}
#Override
public CompositeResult validate(String newPassword) {
CompositeResult result = new CompositeResult(super.delegate.size());
for(Validator rule : super.delegate){
Result temp = rule.validate(newPassword);
if(!temp.isOk())
result.put(rule,temp);
}
return result;
}
public static class CompositeResult extends QuickMap<Validator,Result> implements Result {
private Integer appliedCount;
private CompositeResult(Integer appliedCount) {
super.delegate = VdcCollections.delimitedMap(new HashMap<PasswordRule, Result>(), "-->",", ");
this.appliedCount = appliedCount;
}
#Override
public String getMessage() {
return super.delegate.toString();
}
#Override
public String toString() {
return super.delegate.toString();
}
#Override
public boolean isOk() {
boolean isOk = true;
for (Result r : delegate.values()) {
isOk = r.isOk();
if(!isOk)
break;
}
return isOk;
}
public Integer failCount() {
return this.size();
}
public Integer passCount() {
return appliedCount - this.size();
}
}
}
and now a snippet of use:
private Validator<String> pwRule = new CompositeValidator<String>(new MinLengthValidator(),new NotCurrentValidator());
Validator.Result result = pwRule.validate(newPassword);
if(!result.isOk())
throw new PasswordConstraintException("%s", result.getMessage());
user.obsoleteCurrentPassword();
user.setPassword(passwordEncoder.encode(newPassword));
user.setPwExpDate(DateTime.now().plusDays(passwordDaysToLive).toDate());
userDao.updateUser(user);
Chain of responsibility implies that there is an order in which the validations must take place. I would probably use something similar to the Strategy pattern where you have a Set of validation strategies that are applied to a specific type of record. You could then use a factory to examine the record and apply the correct set of validations.

Categories

Resources