"Whitelabel Error Page" when using SpringDoc with Kotlin and WebFlux - spring

I am getting Whitelabel Error Page when using the SpringBoot + Kotlin + SpringDoc + WebFlux. No documentation is generated despite dependencies from offical springdoc documentation page
enter image description here.
application.yaml does not contain any properties specified
private object Version {
const val kotlinCoroutinesVersion = "1.6.4"
const val openApiVersion = "1.6.12"
}
plugins {
val kotlinVersion = "1.8.10"
id("org.springframework.boot") version "3.0.2"
id("io.spring.dependency-management") version "1.1.0"
id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion
}
dependencies {
// Coroutines dependencies
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Version.kotlinCoroutinesVersion}")
runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:${Version.kotlinCoroutinesVersion}")
// Spring dependencies
implementation("org.springframework.boot:spring-boot-starter-webflux")
// Swagger
implementation("org.springdoc:springdoc-openapi-webflux-ui:${Version.openApiVersion}")
implementation("org.springdoc:springdoc-openapi-kotlin:${Version.openApiVersion}")
// Validation
implementation("javax.validation:validation-api:2.0.1.Final")
}
Example Controller looks like:
#RestController
#RequestMapping("/product")
internal class OrderController(
private val orderCommand: OrderCommand
) {
#PostMapping
suspend fun saveProduct(#Valid #RequestBody createOrderRequest: CreateOrderRequest): ResponseEntity<OrderCreatedResponse> {
// Some code
}
Main function:
#EnableWebFlux
#SpringBootApplication
class Application
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
Any ideas what can I missed? I checked locally on localhost:8080/v3/api-docs.yaml and localhost:8080/swagger-ui.html but still no page has been generated

Adding
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-graphql")
to dependencies solved the issue

Related

Can't call AppEngine Image API getServingUrl method when using SpringBoot stack

Our project needs to serve scaled images. AppEngine ImageApi has a wonderful method called getServingUrl. But this API is only accessible from AppEngine.
Problem
I've created a PoC project with SpringBoot and gradle for AppEngine. And whenever I call a simple endpoint that should call getServingUrl I get the following error:
Can’t make API call blobstore.CreateEncodedGoogleStorageKey in a thread that is neither the original request thread nor a thread created by ThreadManage
But when I do a similar setup that is based on Servlets, it works as expected.
SpringBoot Setup
Here is an example of some files from my setup.
build.gradle:
;; https://github.com/GoogleCloudPlatform/app-gradle-plugin
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.2.0'
}
}
plugins {
id 'org.springframework.boot' version '2.2.6.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
apply plugin: 'com.google.cloud.tools.appengine' // App Engine tasks
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation "com.google.appengine:appengine-api-1.0-sdk:2.0.4"
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
appengine { // App Engine tasks configuration
deploy { // deploy configuration
projectId = 'GCLOUD_CONFIG' // delegate to project in gcloud config
version = 'GCLOUD_CONFIG' // delegate to gcloud to generate a version
}
}
appengine/app.yml
runtime: java11
Controller
package com.example.demo.controller;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.ServingUrlOptions;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ImageController {
#RequestMapping("/image-url")
public ImageUrlDto hello(#RequestParam String bucket, #RequestParam String image) {
ServingUrlOptions options = ServingUrlOptions.Builder
.withGoogleStorageFileName(String.format("/gs/%s/%s", bucket, image));
return new ImageUrlDto(ImagesServiceFactory.getImagesService().getServingUrl(options));
}
}
class ImageUrlDto {
String url;
public ImageUrlDto(String url) {
this.url = url;
}
}
Question
Servlet setup is working correctly, but spring setup no. I've read that Image API is accessible only within AppEngine standart environemnt and request thread/ThreadManager spawned thread.
Am I missing something for that Spring setup? Probably that requires some another form of deployment or thread configuration in order to work with Image API?

NoSuchMethodException QueryDSL with Spring Boot & Spring Data Mongo

I am trying to implement Query DSL on my Spring Boot 2.0.4.RELEASE app that uses Spring Data Mongo 2.0.4.RELEASE & Gradle 4.10.
I am using Spring Tool Suite for running it locally.
Did the following steps which I found from multiple sources including Spring data documentation:
created gradle/querydsl.gradle which has below content to generate Q classes
apply plugin: "com.ewerk.gradle.plugins.querydsl"
sourceSets {
main {
java {
srcDir "$buildDir/generated/source/apt/main"
}
}
}
querydsl {
springDataMongo = true
querydslSourcesDir = "$buildDir/generated/source/apt/main"
}
dependencies {
compile "com.querydsl:querydsl-mongodb:4.1.4"
compileOnly "com.querydsl:querydsl-apt:4.1.4"
}
sourceSets.main.java.srcDirs = ['src/main/java']
Calling above gradle file from main build.gradle as shown below
buildscript {
ext { springBootVersion = "2.0.4.RELEASE" }
repositories { mavenCentral() }
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.9"
}
}
plugins {
id "java"
id "eclipse"
id "org.springframework.boot" version "2.0.4.RELEASE"
id "io.spring.dependency-management" version "1.0.6.RELEASE"
}
sourceCompatibility = 1.8
repositories { mavenCentral() }
dependencies {
...
compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-data-mongodb:${springBootVersion}")
...
}
apply from: 'gradle/querydsl.gradle'
/* Added this because Eclipse was not able to find generated classes */
sourceSets.main.java.srcDirs = ['build/generated/source/apt/main','src/main/java']
compileJava.dependsOn processResources
processResources.dependsOn cleanResources
After this updated the Repository annotated interface as below. Note: I also use Fragment Repository FragmentOrderRepository for some custom queries.
public interface OrderRepository<D extends OrderDAO>
extends EntityRepository<D>, PagingAndSortingRepository<D, String>, FragmentOrderRepository<D>, QuerydslPredicateExecutor<D> {}
Then in controller created a GET mapping as shown here
#RestController
public class OrderController {
#GetMapping(value="/orders/dsl", produces = { "application/json" })
public ResponseEntity<List> getOrdersDSL(#QuerydslPredicate(root = OrderDAO.class) Predicate predicate, Pageable pageable, #RequestParam final MultiValueMap<String, String> parameters) {
return (ResponseEntity<List>) orderService.getTools().getRepository().findAll(predicate, pageable);
}
}
Then in my runner class I added EnableSpringDataWebSupport annotation
#SpringBootApplication
#EnableSpringDataWebSupport
public class SampleApp {
public static void main(String[] args) {
SpringApplication.run(SampleApp.class, args);
}
}
With this my app starts up without any errors but when I try hitting the path http://localhost:5057/orders/dsl?email=test#test.com
I get a NoSuchMethodException with message No primary or default constructor found for interface com.querydsl.core.types.Predicate.
Can anyone please help with some pointers to solve this issue?
It seems that parameters are not getting resolved to a type.
---- UPDATE 09/19/19 ----
While debugging I found that a class HandlerMethodArgumentResolverComposite which finds ArgumentResolver for given MethodParameter from a List of argumentResolvers(of type HandlerMethodArgumentResolver). This list does not contain QuerydslPredicateArgumentResolver. Hence it is not able to resolve the arguments.
This means QuerydslWebConfiguration which adds above resolver for Predicate type is not getting called, which in turn indicates that some AutoConfiguration is not happening.
Probably I am missing some annotation here.
Found the mistake I was doing, was missing EnableWebMvc annotation on my Configuration annotated class.
Details are in this documentation.

Tesseract in Spring Boot Application gives nonsense results for Japanese

I'm writing a Spring Boot Application that uses Bytedeco's Java Wrapper for Tesseract OCR to parse Japanese text. I've managed to get Tesseract working fine when running outside of Spring Boot, but when I use it from within the Spring Boot application it gives me nonsense results.
For example, given the following image:
If I run the following function, the result is reasonable:
fun main() {
val api = tesseract.TessBaseAPI()
api.Init("src/main/resources/tessdata", "jpn_vert")
api.SetPageSegMode(tesseract.PSM_SINGLE_BLOCK_VERT_TEXT)
val pixImage = lept.pixRead("src/main/resources/image.png")
api.SetImage(pixImage)
val result = api.GetUTF8Text()
System.out.println("Parsed text: " + result?.string)
}
Prints:
Parsed text:
坊っちゃん
夏目 滞 石
If I run it from within a Spring Boot Web Socket, however, the result is not:
#SpringBootApplication
open class BootApplication
fun main(args: Array<String>) {
runApplication<BootApplication>(*args)
}
#Configuration
#EnableWebSocket
open class WebSocketConfiguration: WebSocketConfigurer {
#Bean
open fun createWebSocketContainer(): ServletServerContainerFactoryBean {
val container = ServletServerContainerFactoryBean()
container.maxBinaryMessageBufferSize = 1024000
return container
}
override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
registry.addHandler(Endpoint(), "/parse").setAllowedOrigins("*")
}
}
class Endpoint: AbstractWebSocketHandler() {
#Throws(IOException::class)
override fun handleBinaryMessage(session: WebSocketSession?, message: BinaryMessage?) {
// Same code as above:
val api = tesseract.TessBaseAPI()
api.Init("src/main/resources/tessdata", "jpn_vert")
api.SetPageSegMode(tesseract.PSM_SINGLE_BLOCK_VERT_TEXT)
val pixImage = lept.pixRead("src/main/resources/image.png")
api.SetImage(pixImage)
val result = api.GetUTF8Text()
System.out.println("Parsed text:\n" + result?.string)
}
}
Prints the following when handleBinaryMessage is called:
Parsed text:
蝮翫▲縺。繧?繧?
螟冗岼 貊? 遏ウ
I ran a quick test on some English text and that worked fine, so I assume this issue is language-specific.
I'm running the Boot application with the bootRun task from the Spring Boot Gradle plugin which starts an Apache Tomcat service. My first thought is that this has something to do with the fact that the Tesseract wrapper is a JNI library and the environment it's running in (Tomcat) isn't the same. If that's the case, is there some extra configuration that needs to be done to get Tesseract to work with Spring Boot and Tomcat?
For reference, my build.gradle is as follows:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.10'
id("org.springframework.boot") version "2.1.0.RELEASE"
}
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation group: "org.bytedeco.javacpp-presets", name: "tesseract", version: "4.0.0-rc2-1.4.3"
implementation group: "org.bytedeco.javacpp-presets", name: "tesseract", version: "4.0.0-rc2-1.4.3", classifier: "windows-x86_64"
implementation group: "org.bytedeco.javacpp-presets", name: "leptonica", version: "1.76.0-1.4.3", classifier: "windows-x86_64"
implementation group: "org.springframework.boot", name: "spring-boot", version: "2.1.0.RELEASE"
implementation group: "org.springframework.boot", name: "spring-boot-starter-web", version: "2.1.0.RELEASE"
implementation group: "org.springframework", name: "spring-websocket", version: "5.1.2.RELEASE"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
Edit
Looks like it was an encoding issue. Java's file.encoding system property was set to UTF-8 when running from outside of Boot, but set to windows-31j when in Boot. Switching result?.string to result?.getString("UTF-8") fixed it.
I'm going to chalk this up to a bug with Bytedeco's Tesseract wrapper. I did the equivalent test with tess4j and had no problems:
val imageFile = File("src/main/resources/image.png")
val tess = Tesseract()
tess.setPageSegMode(PSM_SINGLE_BLOCK_VERT_TEXT)
tess.setLanguage("jpn_vert")
System.out.println("Parsed text:\n" + tess.doOCR(imageFile))
Gives me:
坊っちゃん
夏目 滞 石
as expected.
I've filed a ticket on their github, so hopefully this will be cleared up before long.

Spock test fails when using JPA Composite Key on Groovy/Gradle build

I developed a Spring Boot WebApplication that receives a soap request with some data and save it to database.
As I am a kind of beginner on Spring Boot and JPA, I used a commonly found configuration for JPA where the primary key is an Id managed by JPA.
#Table(
name="MyEntity",
uniqueConstraints=#UniqueConstraint(columnNames=["channel", "service"])
)
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id
String channel
String service
I developed a set of Spock test about saving, searching and modifying entity and everything runs smoothly.
Specifically I have a Spring Boot application smoke test to be sure that configuration load correctly
#ContextConfiguration
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApplicationContextSpec extends Specification {
#Autowired
WebApplicationContext context
def "should boot up without errors"() {
expect: "(web) application context exists"
context != null
}
}
As the natural key is channel and service, I tried to define a composite key
#Table(name="MyEntity")
#IdClass(MyEntityPk.class)
public class MyEntity {
#Id
String channel
#Id
String service
along with the primary key class (excerpt)
public class MyEntityPk implements Serializable {
String channel
String service
#ContextConfiguration
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApplicationContextSpec extends Specification {
#Autowired
WebApplicationContext context
def "should boot up without errors"() {
expect: "(web) application context exists"
context != null
}
}
After this change, Spring Boot application smoke test fails with
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
...
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
along with all other jpa related tests.
As a further information, I picked Spring Boot gs-accessing-data-jpa-master sample and adapter it to use gradle and spock. Then I modified to use a composite key and Spring Boot application smoke test is successful as expected.
On my Ws Web Application (uses dependency spring-boot-starter-web-services) there is something that causes failure. I would like to point out that the only changes were moving from auto generated key to composite key.
This causes failure to find EmbeddedServletContainerFactory. Do you have any suggestion on the matter ?
I am posting also che build script, quite complex so I added some comments to clarify implementation
buildscript {
ext {
springBootVersion = '1.5.2.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'groovy'
apply plugin: 'org.springframework.boot'
// I am using a mixed environment with some java generated source code
// and mainly groovy classes. This sourceSets definition forces to compile
// everything with groovy compiler so that java classes see groovy classes
sourceSets {
main {
groovy {
srcDirs = ['src/main/groovy', 'src/main/java']
}
java {
srcDirs = []
}
}
test {
groovy {
srcDirs = ['src/test/groovy','src/test/java']
}
java {
srcDirs = []
}
}
}
repositories {
mavenCentral()
}
// This task defines a jaxb task that takes xsd schema definition
// and generates java classes used when mapping soap requests
task genJaxb {
ext.sourcesDir = "${buildDir}/generated-sources/jaxb"
ext.classesDir = "${buildDir}/classes/jaxb"
ext.schema = "src/main/resources/myws.xsd"
outputs.dir classesDir
doLast() {
project.ant {
taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
classpath: configurations.jaxb.asPath
mkdir(dir: sourcesDir)
mkdir(dir: classesDir)
xjc(destdir: sourcesDir, schema: schema) {
arg(value: "-wsdl")
produces(dir: sourcesDir, includes: "**/*.java")
}
javac(destdir: classesDir, source: 1.6, target: 1.6, debug: true,
debugLevel: "lines,vars,source",
classpath: configurations.jaxb.asPath) {
src(path: sourcesDir)
include(name: "**/*.java")
include(name: "*.java")
}
copy(todir: classesDir) {
fileset(dir: sourcesDir, erroronmissingdir: false) {
exclude(name: "**/*.java")
}
}
}
}
}
configurations {
jaxb
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-web-services")
compile('org.springframework.boot:spring-boot-starter-actuator')
compile("org.codehaus.groovy:groovy-all:2.4.7")
compile("wsdl4j:wsdl4j:1.6.1")
compile(files(genJaxb.classesDir).builtBy(genJaxb))
runtime('com.h2database:h2')
jaxb("org.glassfish.jaxb:jaxb-xjc:2.2.11")
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4'
testCompile 'cglib:cglib-nodep:3.2.5'
}
jar {
baseName = 'myws'
version = '0.7.1'
excludes = ['**/application.yml']
from sourceSets.main.output
// adding jaxb generated file to be included in jar
from('build/classes/jaxb') {
include '**'
}
}
// this defines a uber-jar so that I may launch spring server
// from command line using java -jar myws-spring-boot.jar
task('execJar', type:Jar, dependsOn: 'jar') {
baseName = 'myws'
version = '0.7.1'
classifier = 'spring-boot'
from sourceSets.main.output
from('build/classes/jaxb') {
include '**'
}
}
bootRepackage {
withJarTask = tasks['execJar']
}
Eventually, all I have to do is:
gradlew build
to build the jar and run the tests and then
java -jar myws-spring-boot.jar
to launch the spring boot server
Smoke test was not working because I did not perform all required changes when moving from the "Long id" field as primary key
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id
to
#Id
String channel
#Id
String service
I forgot to remove a method in the repository class that was referencing the "Long id" field. After moving to spring-spock 1.1 following Leonard Brünings' suggestion, I got a more detailed error message that pointed me to the right direction.
Error creating bean with name 'modelEntryRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property id found for type ModelEntryEntity!
So I removed the references (method declaration in repository interface) and smoke test ended successfully.
By the way, within build.gradle I had to add spock core explicitly, so instead of one line with reference to spring-spock 1.0 I had to put
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
testCompile 'org.spockframework:spock-spring:1.1-groovy-2.4'

Can't serve dynamic Web Content with Spring Boot and Kotlin

Based on a Spring Boot tutorial to served dynamic web content, I wanted to do the same in Kotlin. My Kotlin project is based on this tutorial. I've no issue running the code of both tutorials.
From my understanding, I only needed to add a controller that would return a reference to a template.
Here HelloController.kt (located under src/main/kotlin/foo/controller) :
package foo.controller
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
#Controller
class HelloController {
private val log = LoggerFactory.getLogger(HelloController::class.java)
#RequestMapping("/")
fun hello(): String {
log.info("foo")
return "index"
}
}
Here is the simple "template", index.html that I want to access (located under src/main/resources/templates/index.html):
<html>
<head>
</head>
<body>
Bar
</body>
</html>
So technically, if I go to localhost:8080 I should have index.html displayed which I don't. Instead I have a 404 error. I do have the logged displayed so the hellomethod is called. What am I doing wrong? I didn't see any configuration file in the Spring Boot tutorial so I guess Spring is doing something under the hood to get the correct ressource from what a function returns.
EDIT:
Has requested my graddle imports:
buildscript {
val springBootVersion = "1.4.3.RELEASE"
val kotlinVersion = "1.0.6"
extra["kotlinVersion"] = kotlinVersion
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
classpath("org.jetbrains.kotlin:kotlin-noarg:$kotlinVersion")
classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
}
}
apply {
plugin("kotlin")
plugin("kotlin-spring")
plugin("kotlin-jpa")
plugin("org.springframework.boot")
}
version = "0.0.1-SNAPSHOT"
configure<JavaPluginConvention> {
setSourceCompatibility(1.8)
setTargetCompatibility(1.8)
}
repositories {
mavenCentral()
}
val kotlinVersion = extra["kotlinVersion"] as String
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("com.h2database:h2")
compile("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
compile("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
compile("org.apache.commons:commons-io:1.3.2")
compile("org.apache.commons:commons-lang3:3.3.1")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
I looks like spring-boot-starter-web dependency is not enough to setup view resolving. Try to add spring-boot-starter-thymeleaf dependency and Thymeleaf should handle your html file.
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
Your HTML files should be in src/main/resources/templates or they might not be automatically detected.

Resources