Thanks to visit codestin.com
Credit goes to www.mscharhag.com

mscharhag, Programming and Stuff;

A blog about programming and software development topics, mostly focused on Java technologies including Java EE, Spring and Grails.

Posts tagged with Java

  • Tuesday, 15 September, 2020

    Implementing the Proxy Pattern in Java

    The Proxy Pattern

    Proxy is a common software design pattern. Wikipedia does a good job describing it like this:

    [..] In short, a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. [..]

    (Wikipedia)

    UML class diagram:

    proxy pattern

    A client requires a Subject (typically an interface). This subject is implemented by a real implementation (here: RealSubject). A proxy implements the same interface and delegates operations to the real subject while adding its own functionality.

    In the next sections we will see how this pattern can be implemented in Java.

    Creating a simple proxy

    We start with an interface UserProvider (the Subject in the above diagram):

    public interface UserProvider {
        User getUser(int id);
    }

    This interface is implemented by UserProviderImpl (the real implementation):

    public class UserProviderImpl implements UserProvider {
        @Override
        public User getUser(int id) {
            return ...
        }
    }

    UserProvider is used by UsefulService (the client):

    public class UsefulService {
        private final UserProvider userProvider;
    
        public UsefulService(UserProvider userProvider) {
            this.userProvider = userProvider;
        }
        
        // useful methods
    }

    To initialize a UsefulService instance we just have to pass a UserProvider object to the constructor:

    UserProvider userProvider = new DatabaseUserProvider();
    UsefulService service = new UsefulService(userProvider);
    
    // use service

    Now let's add a Proxy object for UserProvider that does some simple logging:

    public class LoggingUserProviderProxy implements UserProvider {
        private final UserProvider userProvider;
    
        public LoggingUserProviderProxy(UserProvider userProvider) {
            this.userProvider = userProvider;
        }
    
        @Override
        public User getUser(int id) {
            System.out.println("Retrieving user with id " + id);
            return userProvider.getUser(id);
        }
    }

    We want to create a proxy for UserProvider, so our proxy needs to implement UserProvider. Within the constructor we accept the real UserProvider implementation. In the getUser(..) method we first write a message to standard out before we delegate the method call to the real implementation.

    To use our Proxy we have to update our initialization code:

    UserProvider userProvider = new UserProviderImpl();
    LoggingUserProviderProxy loggingProxy = new LoggingUserProviderProxy(userProvider);
    UsefulService usefulService = new UsefulService(loggingProxy);
    
    // use service

    Now, whenever UsefulService uses the getUser() method we will see a console message before a User object is returned from UserProviderImpl. With the Proxy pattern we were able to add logging without modifying the client (UsefulService) and the real implementation (UserProviderImpl).

    The problem with manual proxy creation

    The previous solution has a major downside: Our Proxy implementation is bound to the UserProvider interfaces and therefore hard to reuse.

    Proxy logic is often quite generic. Typical use-cases for proxies include caching, access to remote objects or lazy loading.

    However, a proxy needs to implement a specific interface (and its methods). This contradicts with re-usability.

    Solution: JDK Dynamic Proxies

    The JDK provides a standard solution to this problem, called Dynamic Proxies. Dynamic Proxies let us create a implementation for a specific interface at runtime. Method calls on this generated proxy are delegated to an InvocationHandler.

    With Dynamic Proxies the proxy creation looks like this:

    UserProvider userProvider = new DatabaseUserProvider();
    UserProvider proxy = (UserProvider) Proxy.newProxyInstance(
            UserProvider.class.getClassLoader(),
            new Class[]{ UserProvider.class },
            new LoggingInvocationHandler(userProvider)
    );
    UsefulService usefulService = new UsefulService(proxy);

    With Proxy.newProxyInstance(..) we create a new proxy object. This method takes three arguments:

    • The classloader that should be used
    • A list of interfaces that the proxy should implement (here UserProvider)
    • A InvocationHandler implementation

    InvocationHandler is an interface with a single method: invoke(..). This method is called whenever a method on the proxy object is called.

    Our simple LoggingInvocationHandler looks like this:

    public class LoggingInvocationHandler implements InvocationHandler {
    
        private final Object invocationTarget;
    
        public LoggingInvocationHandler(Object invocationTarget) {
            this.invocationTarget = invocationTarget;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(String.format("Calling method %s with args: %s",
                    method.getName(), Arrays.toString(args)));
            return method.invoke(invocationTarget, args);
        }
    }

    The invoke(..) method has three parameters:

    • The proxy object on which a method has been called
    • The method that has been called
    • A list of arguments that has been passed to the called method

    We first log the method and the arguments to stdout. Next we delegate the method call to the object that has been passed in the constructor (note we passed the real implementation in the previous snippet).

    The separation of proxy creation (and interface implementation) and proxy logic (via InvocationHandler) supports re-usability. Note we do not have any dependency to the UserProvider interface in our InvocationHandler implementation. In the constructor we accept a generic Object. This gives us the option to reuse the InvocationHandler implementation for different interfaces.

    Limitations of Dynamic Proxies

    Dynamic Proxies always require an interface. We cannot create proxies based on (abstract) classes.

    If this really a great issue for you can look into the byte code manipulation library cglib. cglib is able to create proxy via subclassing and therefore is able to create proxies for classes without requiring an interface.

    Conclusion

    The Proxy Pattern can be quite powerful. It allows us to add functionality without modifying the real implementation or the client.

    Proxies are often used to add some generic functionality to existing classes. Examples include caching, access to remote objects, transaction management or lazy loading.

    With Dynamic Proxies we can separate proxy creation from proxy implementation. Proxy method calls are delegated to an InvocationHandler which can be re-used.

    Note that in some situations the Proxy Pattern can be quite similar to the Decorator pattern (see this Stackoverflow discussion).

     

  • Monday, 24 August, 2020

    OCR in Java with Tess4J

    Optical character recognition (OCR) is the conversion of images containing text to machine-encoded text. A popular tool for this is the open source project Tesseract. Tesseract can be used as standalone application from the command line. Alternatively it can be integrated into applications using its C++ API. For other programming languages various wrapper APIs are available. In this post we will use the Java Wrapper Tess4J.

    Getting started

    We start with adding the Tess4J maven dependency to our project:

    <dependency>
        <groupId>net.sourceforge.tess4j</groupId>
        <artifactId>tess4j</artifactId>
        <version>4.5.2</version>
    </dependency>

    Next we need to make sure the native libraries required by Tess4j are accessible from our application. Tess4J jar files ship with native libraries included. However, they need to be extracted before they can be loaded. We can do this programmatically using a Tess4J utility method:

    File tmpFolder = LoadLibs.extractTessResources("win32-x86-64");
    System.setProperty("java.library.path", tmpFolder.getPath());

    With LoadLibs.extractTessResources(..) we can extract resources from the jar file to a local temp directory. Note that the argument (here win32-x86-64) depends on the system you are using. You can see available options by looking into the Tess4J jar file. We can instruct Java to load native libraries from the temp directory by setting the Java system property java.library.path.

    Other options to provide the libraries might be installing Tesseract on your system. If you do not want to change the java.library.path property you can also manually load the libraries using System.load(..).

    Next we need to provide language dependent data files to Tesseract. These data files contain trained models for Tesseracts LSTM OCR engine and can be downloaded from GitHub. For example, for detecting german text we have to download deu.traineddata (deu is the ISO 3166-1-alpha-3 country code for Germany). We place one or more downloaded data files in the resources/data directory. 

    Detecting Text

    Now we are ready to use Tesseract within our Java application. The following snippet shows a minimal example:

    Tesseract tesseract = new Tesseract();
    tesseract.setLanguage("deu");
    tesseract.setOcrEngineMode(1);
    
    Path dataDirectory = Paths.get(ClassLoader.getSystemResource("data").toURI());
    tesseract.setDatapath(dataDirectory.toString());
    
    BufferedImage image = ImageIO.read(Main.class.getResourceAsStream("/ocrexample.jpg"));
    String result = tesseract.doOCR(image);
    System.out.println(result);

    First we create a new Tesseract instance. We set the language we want to recognize (here: german). With setOcrEngineMode(1) we tell Tesseract to use the LSTM OCR engine.

    Next we set the data directory with setDatapath(..) to the directory containing our downloaded LSTM models (here: resources/data).

    Finally we load an example image from the classpath and use the doOCR(..) method to perform character recognition. As a result we get a String containing detected characters.

    For example, feeding Tesseract with this photo from the German wikipedia OCR article might produce the following text output.

    ocr-example

    Text output:

    Grundsätzliches [Quelltext bearbeiten]
    Texterkennung ist deshalb notwendig, weil optische Eingabegeräte (Scanner oder Digitalkameras, aber
    auch Faxempfänger) als Ergebnis ausschließlich Rastergrafiken liefern können. d. h. in Zeiten und Spaten
    angeordnete Punkte unterschiedlicher Färbung (Pixel). Texterkennung bezeichnet dabei die Aufgabe, die so
    dargestellten Buchstaben als solche zu erkennen, dh. zu identifizieren und ihnen den Zahlenwert
    zuzuordnen, der ihnen nach üblicher Textcodierung zukommt (ASCII, Unicode). Automatische Texterkennung
    und OCR werden im deutschen Sprachraum oft als Synonym verwendet In technischer Hinsicht bezieht sich
    OCR jedoch nur auf den Teilbereich der Muster vergleiche von separierten Bildteilen als Kandidaten zur
    ( Erkennung von Einzelzeichen. Diesem OCR—Prozess geht eine globale Strukturerkennung voraus, in der
    zuerst Textblöcke von graphischen Elementen unterschieden, die Zeilenstrukturen erkannt und schließlich
    | Einzeizeichen separiert werden. Bei der Entscheidung, welches Zeichen vorliegt, kann über weitere
    \ . Algorithmen ein sprachlicher Kontext berücksichtigt werden
    

    Summary

    Tesseract is a popular open source project for OCR. With Tess4J we can access the Tesseract API in Java. A little bit of set up is required for loading native libraries and downloading Tesseracts LSTM data. After that it is quite easy to perform OCR in Java. If you are not happy with the recognized text it is a good idea to have a look at the Improving the quality of the output section of the Tesseract documentation.

    You can find the source code for the shown example on GitHub.

  • Tuesday, 11 August, 2020

    Extending JUnit 5 (Jupiter)

    In this post we will learn how JUnit 5 extensions work and how we can create custom extensions.

    A look into the past: Runners and Rules

    With JUnit 4 we have the option to run tests with a custom JUnit runner (indicated by the @RunWith annotation). This allows us to modify the way tests are executed with JUnit. However, JUnit runners are not that easy to implement. They also suffer on the major limitation that only one runner can be used in a test.

    With JUnit 4.7 Rules were introduced. Rules use a different concept to customize tests. It is also possible to use multiple rules within a test. So from this point JUnit 4 had two different ways (with different up and downsides) to customize test behavior.

    JUnit 5 introduces extensions

    This whole customization mechanism has changed with JUnit 5 which introduced extensions. Extensions can be added to tests in various ways. The most common way is the @ExtendWith annotation that can be used on test classes or on single test methods. For example:

    @ExtendWith(MyFirstExtension.class)
    public class DemoTest {
    
        @Test
        public void test() {
            // uses MyFirstExtension
        }
    
        @Test
        @ExtendWith(MySecondExtension.class)
        public void test2() {
            // uses MyFirstExtension and MySecondExtension
        }
    }

    Extensions added to the test class will be used for all test methods within the class.

    Multiple extensions can be registered by passing an array of extensions to the @ExtendWith annotation:

    @ExtendWith({ MyFirstExtension.class, MySecondExtension.class })
    public class DemoTest {
        ...
    }

    @ExtendWith is also a repeatable annotation, so it can be added multiple times:

    @ExtendWith(MyFirstExtension.class)
    @ExtendWith(MySecondExtension.class)
    public class DemoTest {
        ...
    }

    Note that @ExtendWith can be composed to other annotations. For example, we can come up with our own annotation that is annotated with @ExtendWith:

    @Retention(RetentionPolicy.RUNTIME)
    @ExtendWith(MockWebServerExtension.class)
    @ExtendWith(MockDatabaseExtension.class)
    @Target(ElementType.TYPE)
    public @interface IntegrationTest {}

    We can now annotate our test with @IntegrationTest and JUnit 5 will run the tests using the two extensions defined in @IntegrationTest:

    @IntegrationTest
    public class DemoTest {
        ...
    }

    While the @ExtendWith annotation is easy to use and works fine in most situations it has a drawback. Sometimes test code needs to interact with an extension or the extension might need some sort of configuration or setup code. This cannot be done if the extension is defined with @ExtendWith.

    In these situations we can create the extension manually, assign it to a field and add the @RegisterExtension annotation. For example lets look at a fictional extension that manages temporary files in a test:

    public class DemoTest {
    
        @RegisterExtension
        static TempFileExtension tempFiles = TempFileExtension.builder()
                .setDirectory("/tmp")
                .deleteAfterTestExecution(true)
                .build();
    
        @Test
        public void test() {
            File f = tempFiles.newTempFile("foobar.tmp");
            ...
        }
    }

    Using a @RegisterExtension on a field gives us the option to configure the extension and to interact with the extension in test methods.

    Creating custom extensions

    Creating a custom extension for JUnit 5 is quite easy. We just have to create a class that implements one or more of JUnits extension interfaces.

    Assume we want to create a simple extension that measures how long a test runs. For this we create a new class that implements the interface InvocationInterceptor.

    public class TestDurationReportExtension implements InvocationInterceptor {
    
        @Override
        public void interceptTestMethod(Invocation<Void> invocation,
                ReflectiveInvocationContext<Method> invocationContext,
                ExtensionContext extensionContext) throws Throwable {
    
            long beforeTest = System.currentTimeMillis();
            try {
                invocation.proceed();
            } finally {
                long afterTest = System.currentTimeMillis();
                long duration = afterTest - beforeTest;
                
                String testClassName = invocationContext.getTargetClass().getSimpleName();
                String testMethodName = invocationContext.getExecutable().getName();
                System.out.println(String.format("%s.%s: %dms", testClassName, testMethodName, duration));
            }
        }
    }

    InvocationInterceptor has various methods with default implementations. We override the implementation of interceptTestMethod(..). This method lets us run code before and after a test method is executed. With the proceed() method of the Invocation method parameter we can proceed with the actual test execution.

    We simply subtract the system time before the test from the system time after the test execution to get the duration. After that, we use the InvocationContext parameter to obtain the names of the test class and test method. With this information we create a formatted output message.

    Now we can extend tests with our TestDurationReportExtension by using the @ExtendWith annotation:

    @ExtendWith(TestDurationReportExtension.class)
    public class DemoTest { .. }

    When running tests, we will now see our extension output for every test method.

    The output for a test with two methods might look like this:

    DemoTest.slowTest: 64ms
    DemoTest.fastTest: 6ms

    Extension interfaces

    InvocationInterceptor is just one various extension interfaces. In this section, we will briefly look over these different interfaces and for what they can be used.

    Conditional test execution

    By implementing the interface ExecutionCondition an extension can decide if a test should be executed. This lets the extension decide if certain tests should be skipped. A simple example is the standard extension DisabledCondition that skips tests annotated with @Disabled.

    Test instance factories

    By default JUnit 5 will instantiate test classes by invoking the available constructor (if multiple test constructors are available an exception will be thrown). Possible constructor arguments are resolved using ParameterResolver extensions (see below).

    This default behavior can be customized using the TestInstanceFactory interface. An Extension that implements TestInstanceFactory is used as factory for creating test class instances. This is can be used to create Tests via static factory methods or to inject additional parameters into the test constructor.

    Processing test instances

    After a test instance has been created, the TestInstancePostProcessor interface can be used to post process test instances. A common extension use case for this is the injection of dependencies into fields of the test instance. Similarly the TestInstancePreDestroyCallback can be used to run custom cleanup logic, when a test has finished and the instance is no longer needed.

    Test parameter resolution

    Test class constructors or methods annotated with @Test, @BeforeEach, @BeforeAll etc. can contain parameters. These parameters are resolved at runtime by JUnit using ParameterResolvers. Extensions can implement ParameterResolver if they want to support additional parameters.

    Test Lifecycle callbacks and interceptions

    JUnit 5 provides a couple of test lifecycle callback interfaces that can be implemented by extensions:

    • BeforeAllCallback, runs before @BeforeAll methods in the test class
    • BeforeEachCallback, runs before @BeforeEach methods in the test class
    • BeforeTestExecutionCallback, runs before the test method
    • AfterTestExecutionCallback, runs after the test method
    • AfterEachCallback, runs after @AfterEach methods in the test class
    • AfterAllCallback, runs after @AfterAll methods in the test class

    Those interfaces provide a simple callback to do something at a certain time in the test lifecycle.

    Additionally there is the InvocationInterceptor interface we already used in the extension example above. InvocationInterceptor has similar methods as the callback interfaces. However, InvocationInterceptor gives us an Invocation parameter that allows us to manually continue the lifecycle by calling the proceed() method. This is useful if we want to wrap code around the invocation, like a try/catch block.

    Summary

    Writing extensions for JUnit 5 is quite easy. We just have to create a class that implements one or more of JUnits extension interfaces. Extensions can be added to test classes (or methods) using the @ExtendWith and @RegisterExtension annotations. You can find the source code for the example extension on GitHub. Also make sure to checkout the excellent JUnit 5 user guide.

  • Monday, 3 August, 2020

    Integrating JSON Schema validation in Spring using a custom HandlerMethodArgumentResolver

    In previous posts we learned about JSON Schema and how we can validate a JSON document against a JSON Schema in Java. In this post we will integrate JSON Schema validation into a Spring Boot application using a custom HandlerMethodArgumentResolver. We will use the same JSON document and JSON Schema as in previous posts.

    So, what is a HandlerMethodArgumentResolver?

    Handler methods in Spring controllers (= methods annotated with @RequestMapping, @GetMapping, etc.) have flexible method signatures. Depending on what is needed inside a controller method, various method arguments can be added. Examples are request and response objects, headers, path variables or session values. Those arguments are resolved using HandlerMethodArgumentResolvers. Based on the argument definition (type, annotations, etc.) a HandlerMethodArgumentResolver is responsible for obtaining the actual value that should be passed to the controller.

    A few standard HandlerMethodArgumentResolvers provided by Spring are:

    • PathVariableMethodArgumentResolver resolves arguments annotated with @PathVariable.
    • Request related method arguments like WebRequest, ServletRequest or MultipartRequest are resolved by ServletRequestMethodArgumentResolve.
    • Arguments annotated with @RequestHeader are resolved by RequestHeaderMapMethodArgumentResolver.

    In the following we will create our own HandlerMethodArgumentResolver implementation that validates a JSON request body against a JSON Schema before the JSON data is passed to a controller method.

    Getting started

    Like in the previously mentionend article about JSON Schema validation in Java we will use the json-schema-validator library:

    <dependency>
        <groupId>com.networknt</groupId>
        <artifactId>json-schema-validator</artifactId>
        <version>1.0.42</version>
    </dependency>

    We start with creating our own @ValidJson annotation. This annotation will be used to mark controller method arguments that should be resolved by our own HandlerMethodArgumentResolver.

    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ValidJson {
        String value();
    }
    

    As we see in the next snippet, the value parameter of our @ValidJson annotation is used to define the path to the JSON Schema. We can now come up with the following controller implementation:

    public class Painting {
        private String name;
        private String artist;
    
       // more fields, getters + setters
    }
    public interface SchemaLocations {
        String PAINTING = "classpath:painting-schema.json";
    }
    @RestController
    public class PaintingController {
    
        @PostMapping("/paintings")
        public ResponseEntity<Void> createPainting(@ValidJson(PAINTING) Painting painting) {
            ...
        }
    }

    Painting is a simple POJO we use for JSON mapping. SchemaLocations contains the location of our JSON Schema documents. In the handler method createPainting we added a Painting argument annotated with @ValidJson. We pass the PAINTING constant to the @ValidJson annotation to define which JSON Schema should be used for validation.

    Implementing HandlerMethodArgumentResolver

    HandlerMethodArgumentResolver is an interface with two methods:

    public interface HandlerMethodArgumentResolver {
    
        boolean supportsParameter(MethodParameter parameter);
    
        Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
    }

    supportsParameter(..) is used to check if this HandlerMethodArgumentResolver can resolve a given MethodParameter, while resolveArgument(..) resolves and returns the actual argument.

    We implement this interface in our own class: JsonSchemaValidatingArgumentResolver:

    public class JsonSchemaValidatingArgumentResolver implements HandlerMethodArgumentResolver {
    
        private final ObjectMapper objectMapper;
        private final ResourcePatternResolver resourcePatternResolver;
        private final Map<String, JsonSchema> schemaCache;
    
        public JsonSchemaValidatingArgumentResolver(ObjectMapper objectMapper, ResourcePatternResolver resourcePatternResolver) {
            this.objectMapper = objectMapper;
            this.resourcePatternResolver = resourcePatternResolver;
            this.schemaCache = new ConcurrentHashMap<>();
        }
    
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            return methodParameter.getParameterAnnotation(ValidJson.class) != null;
        }
        
        ...
    }

    supportsParameter(..) is quite easy to implement. We simply check if the passed MethodParameter is annotated with @ValidJson.

    In the constructor we take a Jackson ObjectMapper and a ResourcePatternResolver. We also create a ConcurrentHashMap that will be used to cache JsonSchema instances.

    Next we implement two helper methods: getJsonPayload(..) returns the JSON request body as String while getJsonSchema(..) returns a JsonSchema instance for a passed schema path.

    private String getJsonPayload(NativeWebRequest nativeWebRequest) throws IOException {
        HttpServletRequest httpServletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        return StreamUtils.copyToString(httpServletRequest.getInputStream(), StandardCharsets.UTF_8);
    }
    
    private JsonSchema getJsonSchema(String schemaPath) {
        return schemaCache.computeIfAbsent(schemaPath, path -> {
            Resource resource = resourcePatternResolver.getResource(path);
            if (!resource.exists()) {
                throw new JsonSchemaValidationException("Schema file does not exist, path: " + path);
            }
            JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
            try (InputStream schemaStream = resource.getInputStream()) {
                return schemaFactory.getSchema(schemaStream);
            } catch (Exception e) {
                throw new JsonSchemaValidationException("An error occurred while loading JSON Schema, path: " + path, e);
            }
        });
    }

    A JsonSchema is retrieved from a Spring Resource that is obtained from a ResourcePatternResolver. JsonSchema instances are cached in the previously created Map. So a JsonSchema is only loaded once. If an error occurs while loading the JSON Schema, a JsonSchemaValidationException is thrown.

    The last step is the implementation of the resolveArgument(..) method:

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        // get schema path from ValidJson annotation
        String schemaPath = methodParameter.getParameterAnnotation(ValidJson.class).value();
    
        // get JsonSchema from schemaPath
        JsonSchema schema = getJsonSchema(schemaPath);
    
        // parse json payload
        JsonNode json = objectMapper.readTree(getJsonPayload(nativeWebRequest));
    
        // Do actual validation
        Set<ValidationMessage> validationResult = schema.validate(json);
    
        if (validationResult.isEmpty()) {
            // No validation errors, convert JsonNode to method parameter type and return it
            return objectMapper.treeToValue(json, methodParameter.getParameterType());
        }
    
        // throw exception if validation failed
        throw new JsonValidationFailedException(validationResult);
    }

    Here we first get the location of the JSON Schema from the annotation and resolve it to an actual JsonSchema instance. Next we parse the request body to a JsonNode and validate it using the JsonSchema. If validation errors are present we throw a JsonValidationFailedException.

    You can find the source code for the complete class on GitHub.

    Registering our HandlerMethodArgumentResolver

    Next we need to tell Spring about our JsonSchemaValidatingArgumentResolver. We do this by using the addArgumentResolvers(..) method from the WebMvcConfigurer interface.

    @Configuration
    public class JsonValidationConfiguration implements WebMvcConfigurer {
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Autowired
        private ResourcePatternResolver resourcePatternResolver;
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(new JsonSchemaValidatingArgumentResolver(objectMapper, resourcePatternResolver));
        }
    }

    Error handling

    In our JsonSchemaValidatingArgumentResolver we throw two different exceptions:

    • JsonSchemaLoadingFailedException if loading the JSON Schema fails
    • JsonValidationFailedException if the JSON validation fails

    JsonSchemaLoadingFailedException very likely indicates a programming error while a JsonValidationFailedException is caused by a client sending an invalid JSON document. So we should clearly send a useful error message to the client if the later occurs.

    We do this by using an @ExceptionHandler method in a class annotated with @ControllerAdvice:

    @ControllerAdvice
    public class JsonValidationExceptionHandler {
    
        @ExceptionHandler(JsonValidationFailedException.class)
        public ResponseEntity<Map<String, Object>> onJsonValidationFailedException(JsonValidationFailedException ex) {
            List<String> messages = ex.getValidationMessages().stream()
                    .map(ValidationMessage::getMessage)
                    .collect(Collectors.toList());
            return ResponseEntity.badRequest().body(Map.of(
                "message", "Json validation failed",
                "details", messages
            ));
        }
    }

    Within the method we retrieve the validation messages from the passed exception and send them to the client.

    Example request

    An example request we can send to our PaintingController looks like this:

    POST http://localhost:8080/paintings
    Content-Type: application/json
    
    {
      "name": "Mona Lisa",
      "artist": null,
      "description": null,
      "dimension": {
        "height": -77,
        "width": 53
      },
      "tags": [
        "oil",
        "famous"
      ]
    }

    Note that the JSON body contains two errors: artist is not allowed to be null and the painting height is negative. (You can look at the JSON Schema on GitHub)

    Our server responds to this request with the following response:

    HTTP/1.1 400 (Bad Request)
    {
      "message": "Json validation failed",
      "details": [
        "$.artist: null found, string expected",
        "$.dimension.height: must have a minimum value of 1"
      ]
    }

    Summary

    We learned how to integrate JSON Schema validation into a Spring Boot application by implementing a custom HandlerMethodArgumentResolver. Within our implementation we validate the JSON request body against a JSON Schema before it is passed as argument to the controller. To return proper error message we can add a @ExceptionHandler method.

    You can find the complete example project on GitHub.

  • Thursday, 23 July, 2020

    JSON Schema validation in Java

    In this post we will see how to validate a JSON document against a JSON Schema in Java. We will use the same JSON document and Schema as in the previous post about JSON Schema.

    You can find both as text files on GitHub: JSON document and JSON Schema.

    We use the networknt JSON Schema validator library in this example. This library seems like a good fit because it supports the latest JSON Schema version (2019-09) and uses Jackson as JSON library. This makes it easy to integrate JSON Schema validation in Spring (hint: upcoming blog post).

    We need to add the following dependency to our project:

    <dependency>
        <groupId>com.networknt</groupId>
        <artifactId>json-schema-validator</artifactId>
        <version>1.0.42</version>
    </dependency>

    Now we can validate our JSON document in Java:

    private static InputStream inputStreamFromClasspath(String path) {
        return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
    }
    
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
    
        try (
                InputStream jsonStream = inputStreamFromClasspath("example.json");
                InputStream schemaStream = inputStreamFromClasspath("example-schema.json")
        ) {
            JsonNode json = objectMapper.readTree(jsonStream);
            JsonSchema schema = schemaFactory.getSchema(schemaStream);
            Set<ValidationMessage> validationResult = schema.validate(json);
    
            // print validation errors
            if (validationResult.isEmpty()) {
                System.out.println("no validation errors :-)");
            } else {
                validationResult.forEach(vm -> System.out.println(vm.getMessage()));
            }
        }
    }

    When obtaining a JsonSchemaFactory we need to pass a VersionFlag. This defines the JSON Schema version we want to use (here: 2019-09).

    We then use a small helper method to load both files from the classpath. A Jackson ObjectMapper instance is used to read the JSON data from the InputStream and parse it into a JsonNode object. From the JsonSchemaFactory we can obtain a JsonSchema object which can then be used validate the JsonNode. In case of validation errors the returned Set will contain one or more ValidationMessage objects. If the returned Set is empty, no validation errors were found.

    If we accidentally set the painting height to a negative number in our JSON document, we will get the following validation message:

    $.dimension.height: must have a minimum value of 1

    You can find the example source code on GitHub.

  • Monday, 13 July, 2020

    Quick tip: ISO 8601 durations in Java

    Many developers know about the interchange formats for dates and times defined by ISO 8601. (For example 2007-08-31T16:47+00:00 which represents 16:47 on August 31, 2007 in UTC)

    However, what is not so well-known (at least in my experience), is that this standard also defines a format for durations.

    Here are a few examples:

    • P1Y - 1 year
    • P2M4D - 2 months and 4 days
    • P3Y6M4DT12H30M5S - 3 years, 7 months, 4 days, 12 hours, 30 minutes, and 5 seconds

    In Java it is quite easy to work with this format because the java.time API can automatically parse it to Duration and Period objects.

    Duration is used to work with hours and smaller time units while Period works with dates.

    For example:

    Duration duration = Duration.parse("PT12H"); // 12 hours
    Period period = Period.parse("P3Y6M"); // 3 years and 6 months

    We can now work with these instances, for example we can add period to a LocalDate:

    LocalDate result = LocalDate.of(2020, 8, 1).plus(period); // 2024-02-01