Validating Spring MVC Request Mapping Method parameters

This short post demonstrates how to set up and use JSR-303 Validation for the arguments in Spring MVC request mapping methods for path variable and request parameter arguments. I am using Spring Boot v1.2.5 with Maven 3.

I. MethodValidationPostProcessor

The only configuration needed is adding the MethodValidationPostProcessor (javadoc) bean to the Spring configuration, e.g.

 @Bean
 public MethodValidationPostProcessor methodValidationPostProcessor() {
      return new MethodValidationPostProcessor();
 }

II Add validation to controller request mapping method

First, add the @Validate annotation to the controller class as follows:

@RestController
@Validated
public class HelloController {
     ...

Then add any JSR-303 validation annotation to a request mapping method arguments:

 @RequestMapping("/hi/{name}")
 public String sayHi(@Size(max = 10, min = 3, message = "name should have between 3 and 10 characters") @PathVariable("name") String name) {
      return "Hi " + name;
 }

The example codes above shows how to validate a value in the request path marked by @PathVariable. You can do the same with @RequestParam

III Validation exception handling

A ConstraintViolationException will be thrown if the size of the path variable is not within 3 to 10 characters. You may need to catch this exception and process it using a Spring MVC exception handler, for example to return the error messages in the response:

 @ExceptionHandler(value = { ConstraintViolationException.class })
 @ResponseStatus(value = HttpStatus.BAD_REQUEST)
 public String handleResourceNotFoundException(ConstraintViolationException e) {
      Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
      StringBuilder strBuilder = new StringBuilder();
      for (ConstraintViolation<?> violation : violations ) {
           strBuilder.append(violation.getMessage() + "\n");
      }
      return strBuilder.toString();
 }
Advertisements

Using Netflix servo to monitor Java applications

Netflix servo is a lightweight API for exposing and publishing application metrics. This blog will demonstrate step by step how to use servo in a Java application.

Project set up

I use Maven so the first step is to include the following in the pom file:

 <dependency>
      <groupId>com.netflix.servo</groupId>
      <artifactId>servo-core</artifactId>
      <version>0.8.0</version>
 </dependency>

Adding Metrics to JMX

Adding metrics with servo is simple. For example, the following codes add metrics to a Rest controller for a single end point (/sayhi):

@RestController("testController")
public class TestController {

 @Monitor(name = "requestCounter", type = DataSourceType.COUNTER, description = "Total number of requests", level = DataSourceLevel.INFO)
 private final AtomicInteger requestCounter = new AtomicInteger(0);
 
 @Monitor(name = "aGauge", type = DataSourceType.GAUGE, description = "A random gauge", level = DataSourceLevel.CRITICAL)
 private final AtomicInteger aGauge = new AtomicInteger(0);
 
 @MonitorTags
 private final TagList tags = BasicTagList.of("id", "testController", "class", "au.com.dac.controller.TestController");
 
 @PostConstruct
 public void init() {
      Monitors.registerObject("testController", this);
 }
 
 @RequestMapping(value = "/sayhi", method = RequestMethod.GET )
 public String sayHi(@RequestParam String to) {
      requestCounter.incrementAndGet(); // increment counter
      aGauge.set(RandomUtils.nextInt(0, 100)); // set some random value
      return "hi " + to;
 }
 
}

Two metrics are defined in this class using the @Monitor annotation. There are 3 types of monitors: counter, gauge and informational. Note also the user of @MonitorTags annotation. This is used to add a set of tags as key-value pairs to all the annotated fields in the class. In the example above, two key-value pairs with key “id” and “class” and values “testController” and “au.com.dac.controller.TestController” respectively.

The object needs to be registered with a monitor registry for it to be monitored. This is done with the following line:

Monitors.registerObject("testController", this);

The registerObject method of the class Monitors use the default monitor registry class DefaultMonitorRegistery to register an object. By default, a JMXMonitorRegistry is used.

To update the monitors, use their corresponding API methods:

 requestCounter.incrementAndGet(); // increment counter
 aGauge.set(RandomUtils.nextInt(0, 100)); // set some random value

Now the metrics are available in JMX and can be viewed via JConsole or VisualVM.

Publishing to other sources

Servo provides a simple and yet powerful API for collecting and publishing metrics to other sources. This makes it easy to add monitors to a Java application and expose them to external monitoring system such as Stackdriver and AWS CloudWatch. The following method setup a metrics poller to collect all metrics regularly (once a minute) and publish to the included metric observers.

 private void initMetricsPublishing() {
      PollScheduler scheduler = PollScheduler.getInstance();
      scheduler.start();
      MetricObserver logObserver = new LoggerMetricObserver("logger-observer"); 
      MetricObserver logObserverRate = new LoggerMetricObserver("logger-observer-rate");
      MetricObserver transform = new CounterToRateMetricTransform(logObserverRate, 1, TimeUnit.MINUTES);
      PollRunnable task = new PollRunnable(
           new MonitorRegistryMetricPoller(),
           BasicMetricFilter.MATCH_ALL,
           logObserver, transform);
      scheduler.addPoller(task, 1, TimeUnit.MINUTES);
 }

Note the poller is associated with two MetricObserver instances logObserver and logObserverRate. The later is wrapped by a CounterToRateMetricTransformer which is used to transform any counter values in the observer into rates.

MetricObserver

This is the interface servo uses to publish metrics to. Servo provides a few example implementations, include one for AWS CloudWatch. The following implements a metric observer that logs some metric information

public class LoggerMetricObserver extends BaseMetricObserver {
 private Logger logger = LoggerFactory.getLogger(getClass());

 public LoggerMetricObserver(String name) {
      super(name);
 }
 @Override
 public void updateImpl(List<Metric> metrics) {
      Preconditions.checkNotNull(metrics, "metrics");
      try {
         for (Metric m : metrics) {
              logger.info("{}: name[{}] tags[{}] value[{}]", new LocalDateTime(m.getTimestamp()), m.getConfig()
 .getName(), m.getConfig().getTags(), m.getValue());
         }
       } catch (Throwable t) {
              incrementFailedCount();
              throw Throwables.propagate(t);
       }
 }
}

Running the Application

The complete source codes can be found in here. Running the web application (embedded Tomcat) and sending requests to http://localhost:8080/sayhi?to=Raymond with the browsers should display on the console something like the below every minute:

2015-08-04 23:48:10.119 INFO 952 --- [PollScheduler-3] a.c.m.b.servo.LoggerMetricObserver : 2015-08-04T23:48:10.119: name[requestCounter] tags[class=TestController,COUNTER,id=testController,INFO] value[3]
2015-08-04 23:48:10.119 INFO 952 --- [PollScheduler-3] a.c.m.b.servo.LoggerMetricObserver : 2015-08-04T23:48:10.119: name[aGauge] tags[class=TestController,GAUGE,id=testController,CRITICAL] value[51]
2015-08-04 23:48:10.120 INFO 952 --- [PollScheduler-3] a.c.m.b.servo.LoggerMetricObserver : 2015-08-04T23:48:10.119: name[requestCounter] tags[class=TestController,RATE,id=testController,INFO] value[0.04987697014032054]
2015-08-04 23:48:10.120 INFO 952 --- [PollScheduler-3] a.c.m.b.servo.LoggerMetricObserver : 2015-08-04T23:48:10.119: name[aGauge] tags[class=TestController,GAUGE,id=testController,CRITICAL] value[51]

Note the log messages for the 2 metric observers.