Skip to main content

Spring WebFlux, Boot 2 and reactive mysql


In this post I will describe an web application which contains of: Spring WebFlux, Spring Boot 2, Mysql r2dbc and a lot of fun.
My set up:
       
           Ubuntu 18.04 64-bit
           Java 8 (Oracle)
           docker 18.09.7
           mysql  Ver 8.0.17 for Linux on x86_64 (MySQL Community Server - GPL)
       
  

Next, Mysql runs as a docker container with exposed ports, I could connect to it outside. I've created a schema and inserted data using *.sql scripts. One for creation if exists and the second one for insertion.

Next, created a gradle project with the following dependencies:
       
      
buildscript {
    ext {
        springFrameworkVersion = '5.2.0.M3'
        springBootVersion = '2.1.9.RELEASE'
        springDataR2dbc = '1.0.0.RC1'
        applicationName  = 'be-board-allocator'
        junitFive = '5.5.2'
    }

    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.9.RELEASE'
    }

    configurations {
        compile.exclude module: "spring-boot-starter-tomcat"
    }
}



plugins {
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
    id 'org.springframework.boot' version '2.1.9.RELEASE'
}

apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'jacoco'


repositories {
    mavenCentral()
    maven { url "http://repo.spring.io/libs-snapshot" }
    maven { url 'https://jitpack.io' }
    maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' }
}

dependencies {
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: springBootVersion
    compile group: 'org.springframework.data', name: 'spring-data-r2dbc', version: springDataR2dbc
    compile group: 'com.github.jasync-sql', name: 'jasync-mysql', version: '1.0.7'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-log4j2', version: springBootVersion
    compile group: 'dev.miku', name: 'r2dbc-mysql', version: '0.8.0.RC1'
    compile group: 'io.netty', name: 'netty-all', version: '4.1.42.Final'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: springBootVersion


    //compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: springBootVersion
    compile 'io.springfox:springfox-swagger2:3.0.0-SNAPSHOT'
    compile 'io.springfox:springfox-swagger-ui:3.0.0-SNAPSHOT'
    compile 'io.springfox:springfox-spring-webflux:3.0.0-SNAPSHOT'

    compile group: 'org.springframework', name: 'spring-tx', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-core', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-webflux', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-web', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-orm', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-jdbc', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-jcl', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-expression', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-context', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-beans', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-aspects', version: springFrameworkVersion
    compile group: 'org.springframework', name: 'spring-aop', version: springFrameworkVersion
    compile group: 'io.projectreactor', name: 'reactor-core', version: '3.3.0.RELEASE'


    testImplementation('org.junit.jupiter:junit-jupiter:5.5.2')
}

configurations {
    compile.exclude module: 'spring-boot-starter-logging'
}

jacoco {
    toolVersion = '0.8.4'
}


jacocoTestReport {
    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it, exclude: ['**/Main*'])
        })
    }
    reports {
        xml.enabled = true
        html.enabled = true
    }
}

test {
    useJUnitPlatform()
    testLogging {
        events "passed", "skipped", "failed"
    }
}
  

Interestingly, r2dbc has not yet fully supported mysql reactive driver. And Spring package manager sucks also, thus I should have added explicitly all Spring reactive dependencies. In addition, I've added support of junit 5.

Next, more challenging step was to add PUT/POST method for the @RestController.

Here is an example with a problem ...
      
    @PutMapping(path = "/vlan")
    public void addVlan(@RequestBody VlanData vlanData) {
        logger.debug("REQUESTED DATA: " + vlanData);
    }
      
  

When you ran a curl command
      
     
       curl -X PUT 127.0.0.1:6767/vlan -d '{"vlan_id":"9f8d4422-0aac-49f1-94b2-21b8302bebc2","vlan":"lbjmuspxig"}'
      
  
You've got some amazing error:
      
     
       {"timestamp":1571234848606,"path":"/vlan","status":500,"error":"Internal Server Error","message":"In a WebFlux application, form data is accessed via ServerWebExchange.getFormData()."}
      
  

Basically for this reason I've written this blog. It took some time to figure out the cause. Having googled nothing that makes sence gotten. I started debugging and found a class: AbstractMessageReaderArgumentResolver with the following checking
      
     
      if (mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) {
 return Mono.error(new IllegalStateException("In a WebFlux application, form data is accessed via ServerWebExchange.getFormData()."));
      }
      
  

That means, because I previous Java code, Put method was not specifying context type, as result ... such unplesant error.

Let's fix the problem!
      
     
    @PutMapping(path = "/vlan", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    public void addVlan(@RequestBody VlanData vlanData) {
        logger.debug("REQUESTED DATA: " + vlanData);
    }
      
  

Happy coding!!!

Comments

Popular posts from this blog

Solution for "Caused by: java.io.IOException: Cannot run program "bazel""

Today I started to play around with TensorFlow 2.0 for Java. TensorFlow - git As usual, git clone git@github.com:tensorflow/java.git And then cd to java Next, nano /home/oleg/Downloads/tika/java/tensorflow-core/tensorflow-core-api/.bazelrc build --incompatible_restrict_string_escapes=false ctrl + X Next, if you've got: ModuleNotFoundError: No module named 'numpy' sudo pip3 install -U numpy And now, just re-build. mvn clean install .