Hot questions for Spring Boot Gradle Plugin

Top 10 Java Open Source / Spring / Spring Boot Gradle Plugin

Question:

What does a correct Gradle configuration look like in a multi-module project that uses the Gradle plugins spring-boot-dependencies and spring-boot?

I have the following project setup:

parent
  |
  + build.gradle
  |
  + alpha
  |   |
  |   + build.gradle
  |
  + beta
  |   |
  |   + build.gradle
  • The parent module contains common project configuration.
  • The alpha module is a module in which I would like to import dependencies using versions numbers specified in the spring-boot-dependencies bom, but the result of the is a standard jar.
  • The beta module is a module that depends on alpha and the result is an executable Spring Boot jar file (including all dependencies). Consequently, this project needs both the spring-boot-dependencies as well as the spring-boot plugin.

In order to keep the Gradle files DRY, I have extracted common module scripts to the parent's build.gradle file.

Attempts to execute $ gradle build using the project configuration below results in:

> Plugin with id 'io.spring.dependency-management' not found.

parent gradle.build

allprojects {
    group = "com.example"
    version '0.0.1-SNAPSHOT'

    ext {
        dependencyManagementPluginVersion = '0.5.3.RELEASE'
        springBootVersion = '1.3.0.RC1'
    }

    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'idea'
}

subprojects {
    sourceCompatibility = 1.8
    targetCompatibility = 1.8

    buildscript {
        repositories {
            jcenter()
            maven { url "https://repo.spring.io/snapshot" }
            maven { url "https://repo.spring.io/milestone" }
        }
        dependencies {
            classpath("io.spring.gradle:dependency-management-plugin:${dependencyManagementPluginVersion}")
            classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        }
    }

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

    dependencyManagement {
        imports {
            mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
//            mavenBom("org.springframework.boot:spring-boot-starter-parent:${springBootVersion}")
        }
    }
}

alpha build.gradle

dependencies {
    compile('org.springframework:spring-web')
}

beta gradle.build

apply plugin: 'spring-boot'

dependencies {
    compile project(':alpha')
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-starter-web')
}

Comments:

  • The behavior of the spring-boot plugin was changed in Spring Boot 1.3.0.M1
  • Gradle version: 2.8
  • Spring Boot version 1.3.0.RC1

Answer:

It turns out that parent/build.gradle should be rearranged in the following way:

buildscript {
    ext {
        dependencyManagementPluginVersion = '0.5.3.RELEASE'
        springBootVersion = '1.3.0.RC1'
    }
    repositories {
        jcenter()
        maven { url "https://repo.spring.io/snapshot" }
        maven { url "https://repo.spring.io/milestone" }
    }
    dependencies {
        classpath("io.spring.gradle:dependency-management-plugin:${dependencyManagementPluginVersion}")
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

allprojects {
    group = "com.example"
    version '0.0.1-SNAPSHOT'

    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'idea'
}

subprojects {
    sourceCompatibility = 1.8
    targetCompatibility = 1.8

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

    dependencyManagement {
        imports {
            mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
//          mavenBom("org.springframework.boot:spring-boot-starter-parent:${springBootVersion}")
        }
    }
}

The problem lies in the fact that buildscript block for subprojects was indeed configured well but... in a wrong place. This subprojects block relates to subprojects but it will be evaluated in the script it was declared, and there were no dependencies declared for the plugin it was trying to apply.

Question:

I'm trying to achieve a simple scenario in my spring boot project build: including / excluding dependencies and packaging war or jar depending on the environment.

So for example, for the environment dev include devtools and package jar, for prod package war etc.

I know it is not XML based configuration anymore and I can basically write if statements in my build.gradle but is there a recommended way of achieving this?

Can I declare some common dependencies and refer them in a single file instead of creating multiple build files?

Is there a best practice changing build configuration based on the build target environment?


Answer:

ext {
    devDependencies = ['org.foo:dep1:1.0', 'org.foo:dep2:1.0']
    prodDependencies = ['org.foo:dep3:1.0', 'org.foo:dep4:1.0']
    isProd = System.properties['env'] == 'prod'
    isDev = System.properties['env'] == 'dev'
}

apply plugin: 'java'

dependencies {
    compile 'org.foo:common:1.0'
    if (isProd) {
       compile prodDependencies
    }
    if (isDev) {
       compile devDependencies
    }
}

if (isDev) tasks.withType(War).all { it.enabled = false }

Question:

I am using Spring Boot 2 in my Gradle project to do a build to jar in Jenkins, and I would like to change the name of that jar file.

By default, Spring Boot 2 used the Gradle property rootProject.name, which can be set in the /settings.gradle file.

However, I would like to change the jar file name, without changing the rootProject.name.

Here are my bootJar and springBoot sections of the build.gradle file:

bootJar {
  launchScript()
}

.

springBoot {
  buildInfo {
    properties {
      artifact = "jarName"
      group = "groupName"
      name = "projectName"
      version = "1.0"
    }
  }
}

Note: artifact is not setting the jar name, as I expected it to, after reading: https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/html/#integrating-with-actuator


Answer:

archiveFileName is the new hotness. Everything else is deprecated.

bootJar {
   archiveFileName = "${archiveBaseName.get()}.${archiveExtension.get()}"
}

or the Kotlin DSL equivalent:

tasks.getByName<org.springframework.boot.gradle.tasks.bundling.BootJar>("bootJar") {
   this.archiveFileName.set("${archiveBaseName.get()}.${archiveExtension.get()}")
}

See:

Question:

I'm using Gradle with Spring Boot 2.0.0.M7 and have the following plugins applied:

buildscript {
    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
        maven { url "https://repo.spring.io/milestone" }
    }
    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.0.M7"
    }
}

plugins {
    id "com.gorylenko.gradle-git-properties" version "1.4.17"
}

spring-boot-starter-actuator dependency is also there. git.properties files is generated correctly to build/main/resoures directory. I've also added property management.info.git.mode=full. Due to official docs, git information should be added to /info endpoint automatically with GitInfoContributor. However none of the above helps and /info endpoint returns empty JSON instead - {}. Any ideas on how this can be fixed?

UPDATE 1: What I've found so far is that if I manually copy git.properties to out/resources, this way it would work, but they are not put there for some reason.

UPDATE 2: When I run with gradle bootRun it works, however when I start it from Intellij IDEA our run gradle clean build which runs the test which checks if those properties are displayed on /info endpoint - it doesn't work.


Answer:

The problem was running the app from IDE. As the properties are generated on the phase when JAR is assembled, they were not included. Running the application via java -jar artifact.jar or gradle bootRun works without any issues.

Thanks @fateddy for help on resolving the issue.

Question:

I have roughly the following setup:

test-utils/build.gradle:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.1.RELEASE'
    }
}

apply plugin: 'java'
apply plugin: 'spring-boot'

dependencies {
    compile ('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.mockito'
        exclude group: 'org.hamcrest'
    }
    compile  'org.mockito:mockito-core:2.0.41-beta'
    compile  'org.assertj:assertj-core:3.3.0'
}

main/build.gradle:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.1.RELEASE'
    }
}

apply plugin: 'java'
apply plugin: 'spring-boot'

dependencies {
    testCompile project(':test-utils')
}

But for some reason it seems that the spring boot plugin forces the mockito version back to 1.x:

# ./gradlew :main:dependencies --configuration=testCompile

testCompile - Compile classpath for source set 'test'.
+--- project :test-utils
     +--- org.springframework.boot:spring-boot-starter-test: -> 1.3.1.RELEASE
     |    +--- junit:junit:4.12
     |    +--- org.springframework:spring-core:4.2.4.RELEASE
     |    \--- org.springframework:spring-test:4.2.4.RELEASE
     |         \--- org.springframework:spring-core:4.2.4.RELEASE
     +--- org.mockito:mockito-core:2.0.41-beta -> 1.10.19
     |    +--- org.hamcrest:hamcrest-core:1.1 -> 1.3
     |    \--- org.objenesis:objenesis:2.1
     \--- org.assertj:assertj-core:3.3.0

If I take the spring boot plugin out of the equation, things work as expected:

# ./gradlew :main:dependencies --configuration=testCompile

testCompile - Compile classpath for source set 'test'.
+--- project :test-utils
     +--- org.springframework:spring-core:4.2.4.RELEASE (*)
     +--- org.springframework:spring-test:4.2.4.RELEASE
     |    \--- org.springframework:spring-core:4.2.4.RELEASE (*)
     +--- junit:junit:4.12
     +--- org.mockito:mockito-core:2.0.41-beta
     |    +--- net.bytebuddy:byte-buddy:1.0.2
     |    \--- org.objenesis:objenesis:2.1
     \--- org.assertj:assertj-core:3.3.0

What is the spring boot plugin doing exactly, and how can I tell it not to?


Answer:

Your main project has Spring Boot's plugin applied to it, so it's using Spring Boot's dependency management. That means it will, by default, use Spring Boot's preferred version of Mockito, irrespective of the version specificied in test-utils.

As described in the documentation, you can override the version of a dependency that Spring Boot manages by setting the relevant property. For Mockito that property is mockito.version. Add the following to your main project:

ext['mockito.version'] = '2.0.41-beta'

Question:

Recently I tried to update my Spring Boot application version from 1.5.10.RELEASE to 2.0.0.RELEASE

Project Environment

  • JDK version 1.8
  • jcenter() repository in Gradle
  • IDE Spring tool Suite (STS) Version: 3.9.2

I have encountered two issues

  1. I got the completion issue below.

    Could not resolve all files for configuration ':compileClasspath'. Could not find org.springframework.boot:spring-boot-starter-mobile:

    Then I checked the latest version of spring-boot-starter-mobile library and there’s no stable version of spring-boot-starter-mobile for Spring Boot 2.

    So I had to declare milestone URL using maven to import version: '2.0.0.M5' and issue has resolved.

    maven { url 'https://repo.spring.io/milestone' }

    Originally I was using jcenter() and is it possible to import milestones using jcenter?

  2. I’m getting the following runtime error when access the mobile device interface below.

org.springframework.mobile.device.Device

public ResponseEntity<?> TestM(@RequestBody MyRequest myRequest, Device device) throws AuthenticationException {



    return ResponseEntity.ok();
}

ERROR

2018-03-12 23:54:58.410 ERROR 13732 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: 
No primary or default constructor found for interface org.springframework.mobile.device.Device] with root cause

java.lang.NoSuchMethodException: org.springframework.mobile.device.Device.<init>()
    at java.lang.Class.getConstructor0(Unknown Source) ~[na:1.8.0_121]
    at java.lang.Class.getDeclaredConstructor(Unknown Source) ~[na:1.8.0_121]
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:209) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:84) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:132) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:870) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:776) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:881) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855) ~[spring-webmvc-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at com.example.security.JwtAuthenticationTokenFilter.doFilterInternal(JwtAuthenticationTokenFilter.java:72) ~[classes!/:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) ~[tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_121]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_121]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.28.jar!/:8.5.28]
    at java.lang.Thread.run(Unknown Source) [na:1.8.0_121]

Is there any major changes regarding the device access in Spring Boot 2? In that case please advise the correct method.


Answer:

I have configured manually and resolved this issue

Device detection

Add the following Java-based configuration to enable device detection in a Spring web application:

@Bean
public DeviceResolverHandlerInterceptor 
        deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Bean
public DeviceHandlerMethodArgumentResolver 
        deviceHandlerMethodArgumentResolver() {
    return new DeviceHandlerMethodArgumentResolver();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
}

@Override
public void addArgumentResolvers(
       List<HandlerMethodArgumentResolver> argumentResolvers) {
   argumentResolvers.add(deviceHandlerMethodArgumentResolver());
}

Inject the Device into your controller

@Controller
public class HomeController {

private static final Logger logger = 
        LoggerFactory.getLogger(HomeController.class);

@RequestMapping("/")
public void home(Device device) {
    if (device.isMobile()) {
        logger.info("Hello mobile user!");
    } else if (device.isTablet()) {
        logger.info("Hello tablet user!");
    } else {
        logger.info("Hello desktop user!");         
    }
}

}

http://projects.spring.io/spring-mobile/

As @erhanasikoglu pointed out;

Since WebMvcConfigurerAdapter is deprecated, only need to implements WebMvcConfigurer interface inside that configuration class. (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.html)