Override DropWizard ConstraintViolation message - java

So I want to change the validation messages used to validate a model through a DropWizard resource.
I'm using java bean validation annotations. For example here is one of the fields I want to validate:
#NotEmpty(message = "Password must not be empty.")
I can test this works as expected using a validator.
However when I use DropWizard to do the validation on the resource it adds some extra stuff to that message. What I see is this - password Password must not be empty. (was null) and I've found the code that does this here - https://github.com/dropwizard/dropwizard/blob/master/dropwizard-validation/src/main/java/io/dropwizard/validation/ConstraintViolations.java
Specifically this method -
public static <T> String format(ConstraintViolation<T> v) {
if (v.getConstraintDescriptor().getAnnotation() instanceof ValidationMethod) {
final ImmutableList<Path.Node> nodes = ImmutableList.copyOf(v.getPropertyPath());
final ImmutableList<Path.Node> usefulNodes = nodes.subList(0, nodes.size() - 1);
final String msg = v.getMessage().startsWith(".") ? "%s%s" : "%s %s";
return String.format(msg,
Joiner.on('.').join(usefulNodes),
v.getMessage()).trim();
} else {
return String.format("%s %s (was %s)",
v.getPropertyPath(),
v.getMessage(),
v.getInvalidValue());
}
}
Is there any way I can override this behaviour? I just want to display the message that I set in the annotation...

Here is a programmatic solution in dropwizard 0.8:
public void run(final MyConfiguration config, final Environment env) {
AbstractServerFactory sf = (AbstractServerFactory) config.getServerFactory();
// disable all default exception mappers
sf.setRegisterDefaultExceptionMappers(false);
// register your own ConstraintViolationException mapper
env.jersey().register(MyConstraintViolationExceptionMapper.class)
// restore other default exception mappers
env.jersey().register(new LoggingExceptionMapper<Throwable>() {});
env.jersey().register(new JsonProcessingExceptionMapper());
env.jersey().register(new EarlyEofExceptionMapper());
}
I think it's more reliable than a config file. And as you can see it also enables back all other default exception mappers.

ConstraintViolationExceptionMapper is the one which uses that method. In order to override it, you need to deregister it and register your own ExceptionMapper.
Remove the exception mapper(s)
Dropwizard 0.8
Add the following to your yaml file. Note that it will remove all the default exception mappers that dropwizard adds.
server:
registerDefaultExceptionMappers: false
Dropwizard 0.7.x
environment.jersey().getResourceConfig().getSingletons().removeIf(singleton -> singleton instanceof ConstraintViolationExceptionMapper);
Create and add your own exception mapper
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
// get the violation errors and return the response you want.
}
}
and add your exception mapper in your application class.
public void run(T configuration, Environment environment) throws Exception {
environment.jersey().register(ConstraintViolationExceptionMapper.class);
}

#ValidationMethod should be useful here. isn't it?
http://www.dropwizard.io/0.9.0/docs/manual/validation.html
#ValidationMethod(message="Password cannot be empty")
#JsonIgnore
public boolean isPasswordProvided() {
return false if password not provided;
}

Related

Unable to resolve variable from properties file when tried to access as function parameter using #Value annotation

This may be silly question to ask but i'm unable to find any satisfactory solution to my problem. In java we don't have the concept of default variables so i am trying to give default value from properties file to my function parameters/arguments using #Value annotation, but i'm always getting null and i'm unable to figure why is this happening. Please help me to solve the issue or provide me some appropriate link/reference which may solve my issue.
MainApplication.java
#SpringBootApplication
public class Application
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(NetappApplication.class, args);
Sample sample = context.getBean(Sample.class);
System.out.println(sample.check(null));
}
}
Sample.java
public interface Sample
{
public String check(String message);
}
SampleImpl.java
#Service
#PropertySource("classpath:app.properties")
public class SampleImpl implements Sample
{
#Value("${test}")
String message1;
#Override
public String check(#Value("${test}") String message)
{
return message;
}
}
app.properties
test=anand
But you are passing null to your method...
Perhaps what you want to do is to assign default value to test in case it's not defined in property file:
#Value("${test:default}");
Then, when properties are autowired by Spring if placeholder resolver doesn't get the value from props file, it will use what is after :.
The best use case for this (that I can think of) is when you create Spring configuration.
Let's say you have a configuration class: for DB access. Simply put:
#Configuration
public class DbConfig {
#Value("${url:localhost}")
String dbUrl;
// rest for driver, user, pass etc
public DataSource createDatasource() {
// here you use some DataSourceBuilder to configure connection
}
}
Now, when Spring application starts up, properties' values are resolved, and as I wrote above you can switch between value from property and a default value. But it is done once, when app starts and Spring creates your beans.
If you want to check incoming argument on runtime, simple null check will be enough.
#Value("${test}")
String message1;
#Override
public String check(String message) {
if (message == null) {
return message1;
}
}

Spring: automatic rollback on checked exceptions

One way to configure Spring to rollback on a non RuntimeExceptions is using #Transactional(rollbackFor=...) annotation on the service classes. The problem with this approach is that we need to define (rollbackFor=...) for almost all the service classes which seems really redundant.
My question: Is there any way to configure a default behaviour for Spring transaction manager to rollback on a non RuntimeException whenever it happens without declaring it on every #Transactional annotation. Something like using #ApplicationException(rollback=true) annotation on an exception class in EJB.
You can't do it for application level with #Transactional , but you can :
variant 1 : extend #Transactional annotation and put it as default value for rollbackfor. But set rollbackFor unchecked exceptions only that you need .With this you can control rollbacks only for case that you sure , and avoid copy past of #Transactional(rollbackFor =MyCheckedException.class)
Like:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Transactional(rollbackFor=MyCheckedException.class)
public #interface TransactionalWithRollback {
}
And use this annotation instead of standard #Transactional.
variant 2 : you can create extension from AnnotationTransactionAttributeSource and override method determineTransactionAttribute:
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae)
//Determine the transaction attribute for the given method or class.
TransactionAttribute see TransactionAttribute api , there is a method
boolean rollbackOn(Throwable ex) Should we roll back on the given exception?
protected TransactionAttribute determineTransactionAttribute(
AnnotatedElement ae) {
return new DelegatingTransactionAttribute(target) {
#Override
public boolean rollbackOn(Throwable ex) {
return (check is exception type as you need for rollback );
}
};
}
Second approach is not so good as first as you do it really global for transaction manager. Better use custom annotation as you can control it any apply only for methods/classes where you really need it. But if you need it in any case use second variant , it will be your default transnational behavior.
This config solves it:
#Configuration
public class MyProxyTransactionManagementConfiguration extends ProxyTransactionManagementConfiguration {
#Bean
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource() {
#Nullable
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
TransactionAttribute ta = super.determineTransactionAttribute(element);
if (ta == null) {
return null;
} else {
return new DelegatingTransactionAttribute(ta) {
#Override
public boolean rollbackOn(Throwable ex) {
return super.rollbackOn(ex) || ex instanceof Exception;
}
};
}
}
};
}
}
This is a similar approach as this answer, i.e. changing the default globally, but with as minimal change to Spring's config as possible, and still leaving the possibility to customize rollback rules per method as usual (with rollbackFor, noRollbackFor etc.).
This is achieved by simply adding a default RollbackRule for Exception.class. Since the rules have precedence according to the exception class hierarchy (the rule for the most specific exception class applicable wins), the new rule has basically lowest precendence, if no other rules are defined on the annotation.
#Configuration
public class MyTransactionManagementConfiguration {
/**
* Note: This custom config does NOT recognize {#code javax.transaction.Transactional} annotations in contrast to
* the original Spring behaviour. Check the original {#code AnnotationTransactionAttributeSource} source code for an idea how to add that.
*
* #see AnnotationTransactionAttributeSource#AnnotationTransactionAttributeSource(boolean)
*/
#Bean
#Primary
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSourceWithDefaultRollBackForAllExceptions() {
return new AnnotationTransactionAttributeSource(
new SpringTransactionAnnotationParser() {
#Override
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = (RuleBasedTransactionAttribute) super.parseTransactionAnnotation(attributes);
List<RollbackRuleAttribute> rules = new ArrayList<>(rbta.getRollbackRules());
rules.add(new RollbackRuleAttribute(Exception.class));
rbta.setRollbackRules(rules);
return rbta;
}
}
);
}
}

#ConditionalOnProperty for lists or arrays?

I'm using Spring Boot 1.4.3 #AutoConfiguration where I create beans automatically based on properties user specifies. User can specify an array of services, where name and version are required fields:
service[0].name=myServiceA
service[0].version=1.0
service[1].name=myServiceB
service[1].version=1.2
...
If the user forgets to specify a required field on even just one service, I want to back-off and not create any beans. Can I accomplish this with #ConditionalOnProperty? I want something like:
#Configuration
#ConditionalOnProperty({"service[i].name", "service[i].version"})
class AutoConfigureServices {
....
}
This is the custom Condition I created. It needs some polishing to be more generic (ie not hardcoding strings), but worked great for me.
To use, I annotated my Configuration class with #Conditional(RequiredRepeatablePropertiesCondition.class)
public class RequiredRepeatablePropertiesCondition extends SpringBootCondition {
private static final Logger LOGGER = LoggerFactory.getLogger(RequiredRepeatablePropertiesCondition.class.getName());
public static final String[] REQUIRED_KEYS = {
"my.services[i].version",
"my.services[i].name"
};
#Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
List<String> missingProperties = new ArrayList<>();
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(context.getEnvironment());
Map<String, Object> services = resolver.getSubProperties("my.services");
if (services.size() == 0) {
missingProperties.addAll(Arrays.asList(REQUIRED_KEYS));
return getConditionOutcome(missingProperties);
}
//gather indexes to check: [0], [1], [3], etc
Pattern p = Pattern.compile("\\[(\\d+)\\]");
Set<String> uniqueIndexes = new HashSet<String>();
for (String key : services.keySet()) {
Matcher m = p.matcher(key);
if (m.find()) {
uniqueIndexes.add(m.group(1));
}
}
//loop each index and check required props
uniqueIndexes.forEach(index -> {
for (String genericKey : REQUIRED_KEYS) {
String multiServiceKey = genericKey.replace("[i]", "[" + index + "]");
if (!resolver.containsProperty(multiServiceKey)) {
missingProperties.add(multiServiceKey);
}
}
});
return getConditionOutcome(missingProperties);
}
private ConditionOutcome getConditionOutcome(List<String> missingProperties) {
if (missingProperties.isEmpty()) {
return ConditionOutcome.match(ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
.found("property", "properties")
.items(Arrays.asList(REQUIRED_KEYS)));
}
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
.didNotFind("property", "properties")
.items(missingProperties)
);
}
}
Old question, but I hope my answer will help for Spring2.x:
Thanks to #Brian, I checked migration guide, where I was inspired by example code. This code works for me:
final List<String> services = Binder.get(context.getEnvironment()).bind("my.services", List.class).orElse(null);
I did try to get List of POJO (as AutoConfigureService) but my class differs from AutoConfigureServices. For that purpose, I used:
final Services services = Binder.get(context.getEnvironment()).bind("my.services", Services.class).orElse(null);
Well, keep playing :-D
Here's my take on this issue with the use of custom conditions in Spring autoconfiguration. Somewhat similar to what #Strumbels proposed but more reusable.
#Conditional annotations are executed very early in during the application startup. Properties sources are already loaded but ConfgurationProperties beans are not yet created. However we can work around that issue by binding properties to Java POJO ourselves.
First I introduce a functional interface which will enable us to define any custom logic checking if properties are in fact present or not. In your case this method will take care of checking if the property List is empty/null and if all items within are valid.
public interface OptionalProperties {
boolean isPresent();
}
Now let's create an annotation which will be metannotated with Spring #Conditional and allow us to define custom parameters. prefix represents the property namespace and targetClass represents the configuration properties model class to which properties should be mapped.
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Conditional(OnConfigurationPropertiesCondition.class)
public #interface ConditionalOnConfigurationProperties {
String prefix();
Class<? extends OptionalProperties> targetClass();
}
And now the main part. The custom condition implementation.
public class OnConfigurationPropertiesCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
MergedAnnotation<ConditionalOnConfigurationProperties> mergedAnnotation = metadata.getAnnotations().get(ConditionalOnConfigurationProperties.class);
String prefix = mergedAnnotation.getString("prefix");
Class<?> targetClass = mergedAnnotation.getClass("targetClass");
// type precondition
if (!OptionalProperties.class.isAssignableFrom(targetClass)) {
return ConditionOutcome.noMatch("Target type does not implement the OptionalProperties interface.");
}
// the crux of this solution, binding properties to Java POJO
Object bean = Binder.get(context.getEnvironment()).bind(prefix, targetClass).orElse(null);
// if properties are not present at all return no match
if (bean == null) {
return ConditionOutcome.noMatch("Binding properties to target type resulted in null value.");
}
OptionalProperties props = (OptionalProperties) bean;
// execute method from OptionalProperties interface
// to check if condition should be matched or not
// can include any custom logic using property values in a type safe manner
if (props.isPresent()) {
return ConditionOutcome.match();
} else {
return ConditionOutcome.noMatch("Properties are not present.");
}
}
}
Now you should create your own configuration properties class implementing OptionalProperties interface.
#ConfigurationProperties("your.property.prefix")
#ConstructorBinding
public class YourConfigurationProperties implements OptionalProperties {
// Service is your POJO representing the name and version subproperties
private final List<Service> services;
#Override
public boolean isPresent() {
return services != null && services.stream().all(Service::isValid);
}
}
And then in Spring #Configuration class.
#Configuration
#ConditionalOnConfigurationProperties(prefix = "", targetClass = YourConfigurationProperties.class)
class AutoConfigureServices {
....
}
There are two downsides to this solution:
Property prefix must be specified in two locations: on #ConfigurationProperties annotation and on #ConditionalOnConfigurationProperties annotation. This can partially be alleviated by defining a public static final String PREFIX = "namespace" in your configuration properties POJO.
Property binding process is executed separately for each use of our custom conditional annotation and then once again to create the configuration properties bean itself. It happens only during app startup so it shouldn't be an issue but it still is an inefficiency.
You can leverage the org.springframework.boot.autoconfigure.condition.OnPropertyListCondition class. For example, given you want to check for the service property having at least one value:
class MyListCondition extends OnPropertyListCondition {
MyListCondition() {
super("service", () -> ConditionMessage.forCondition("service"));
}
}
#Configuration
#Condition(MyListCondition.class)
class AutoConfigureServices {
}
See the org.springframework.boot.autoconfigure.webservices.OnWsdlLocationsCondition used on org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration#wsdlDefinitionBeanFactoryPostProcessor for an example within Spring itself.

Specifying JAXB 2 context in Jersey 1.17

I'm using Jersey 1.17 on the server side to process REST requests and JAXB 2 to unmarshall the XML request content.
Context
This is the Jersey method I use. The MyDTO class uses the #XmlRootElement annotation (otherwise, I'd need to define the parameter with the JAXBElement type).
#Path("/myService")
#POST
#Consumes(MediaType.APPLICATION_XML)
public void myService(MyDTO dto) throws Exception
{
// Shouldn't get this far if the XML content in the request was invalid
System.out.println(dto);
}
Requirement
By default, the Sun/Oracle JAXB implementation doesn't throw exceptions when the XML content has errors. For example providing a string value, say ABC, for an Integer attribute simply leaves the value as null instead of throwing an exception.
In JAXB 2 a ValidationEvenHandler can be defined. Using the following handler handler, makes the XML unmarshalling throw an exception the way I need it to.
public class UnmarshallerValidationEventHandler implements ValidationEventHandler {
#Override
public boolean handleEvent(ValidationEvent event) {
// This indicates JAXB that it should continue processing only if the
// severity level is less than error. NOTE: validation event constants
// go in ascending order in level of severity(i.e., 0 WARNING, 1: ERROR, 2 :FATAL_ERROR)
return event.getSeverity() < ValidationEvent.ERROR;
}
}
Question
How can I get Jersey to use a particular JAXBContext instance in order to use an unmarshaller with my custom validation event handler?
Alternatively, given that my application only uses JAXB in Jersey methods, defining a particular JAXBContext globally for the the JVM instance would be a good option. How could that be done?
Jersey Users Guide covers this in Using custom JAXBContext chapter. Basically you need to provide ContextResolver<T> like:
#Provider
public class PlanetJAXBContextProvider implements ContextResolver<JAXBContext> {
private JAXBContext context = null;
public JAXBContext getContext(Class<?> type) {
if(type != Planet.class)
return null; // we don't support nothing else than Planet
if(context == null) {
try {
context = JAXBContext.newInstance(Planet.class);
} catch (JAXBException e) {
// log warning/error; null will be returned which indicates that this
// provider won't/can't be used.
}
}
return context;
}
}
You can see a sample use in storage-service sample project (see JAXBContextResolver).
Note: Instead of ContextResolver<JAXBContext> you can also provide ContextResolver<Marshaller> or/and ContextResolver<Unmarshaller>.

Mapping of custom exception in JAX-RS via #WebFault annotation

I am developing a Client-Server app with JAX-RS / Apache CXF, JSON
I would like Apache CXF to handle my exception transparently on both ends : Which means transforming the exception into a bean, serializing it with my Jackson Serializer (JSON) and then doing the over way around on client side.
I have seen several confusing posts/answers on this subject and came up with using the #WebFault annotation :
#WebFault(name=CODE, faultBean="foo.bar.FaultBean")
public class DuplicateRuleNameFault extends Exception {
static final public String CODE = "DUPLICATE_RULE_NAME";
private FaultBean faultBean;
public DuplicateRuleNameFault(String msg) {
super(msg);
this.faultBean = new FaultBean(msg);
}
public DuplicateRuleNameFault() {
}
public FaultBean getFaultBean() {
return faultBean;
}
public void setFaultBean(FaultBean faultBean) {
this.faultBean = faultBean;
}
}
With no success ... Currently, CXF seems to happily ignore the annotation on the Exception and handle it as an unknown exception : 500 status error and no response body generated on the server side.
Is there something specific I have to configure in the "" server element of my Spring context ? I already have Spring scanning my Exception/FaultBean classes (is it even needed BTW ?).
I would appreciate if you could point me at some working example.
Thanks.
#WebFault's are not part of the JAX-RS specification. You will want to read up on section 3.3.4 of the specification, which describes the different ways you can accomplish what you are trying to do.
Option 1
Design your resource classes to throw WebApplicationException's. Set the response property of these exceptions to be a valid JAX-RS response containing the fault beans you want to send to the client.
Option 2
Define exception mapping providers. You can create a hierarchy of these to handle all the common exceptions your application will throw. Or you can create a top level exception with an embedded bean and an exception handler for it. And then derive several specific exceptions from the top level one.
public abstract class MyApplicationException<T> extends Exception {
private T faultBean;
// Constructors, setters/getters
}
#Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException<?>> {
// Implementation
}
One way of doing this is by using the javax.ws.rs.core.Response object like so :
#GET
#Path("/")
public Response getBlah()
{
try {
return Response.status(Response.Status.OK)
.entity(<Object you want to return>).build();
}
catch (final DuplicateRuleNameFault e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(e.getFaultBean().getMsg()).build();
}
}

Categories