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

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'

Related

springboot thin jar, causing problems with injection

#Slf4j
#RestController
#RequestMapping("/admin/app")
public class AppSessionController {
private OnlineSessionService onlineSessionService;
#Autowired
public void setOnlineSessionService(OnlineSessionServiceImpl onlineSessionService) {
log.info("AppSessionController{}",this);
log.info("inject{}",onlineSessionService);
this.onlineSessionService = onlineSessionService;
log.info("after inject{}",this.onlineSessionService);
}
#PreAuthorize("hasAuthority('app:user:getOnlineUserList')")
#PostMapping("/getOnlineUserList")
Result<PageInfo<AuthData<?>>> getOnlineUserList(#RequestBody PageRequest pageRequest) {
log.info("AppSessionController{}, {}",this, this.ss);
log.info("this.onlineSessionService :"+this.onlineSessionService);
log.info("onlineSessionService :"+onlineSessionService);
PageInfo<AuthData<?>> pageInfo = onlineSessionService.getOnlineUserList(pageRequest.getPageNum(), pageRequest.getPageSize());
PageInfoFixUtil.fixPageInfo(pageInfo);
return Result.ok(pageInfo);
}
}
The output:
INFO main (AppSessionController.java:32) - AppSessionControllercom.apex.app.admin.controller.AppSessionController#5a438c0a
INFO main (AppSessionController.java:33) - inject com.apex.app.common.service.impl.OnlineSessionServiceImpl#14cd8dfa
INFO main (AppSessionController.java:36) - after inject com.apex.app.common.service.impl.OnlineSessionServiceImpl#14cd8dfa
INFO http-nio-8100-exec-5 (AppSessionController.java:48) - AppSessionControllercom.apex.app.admin.controller.AppSessionController#5a438c0a, null
INFO http-nio-8100-exec-5 (AppSessionController.java:49) - this.onlineSessionService :null
INFO http-nio-8100-exec-5 (AppSessionController.java:50) - onlineSessionService :null
The code with the problem is as above, why the onlineSessionService is null after the jar is thinned, and other controllers in the same level directory do not have this problem.
If I change the onlineSessionService to static then the injection can be injected.
gradle thin jar method:
// Copy the dependencies to the lib directory
task copyJar(type: Copy) {
// delete lib directory
delete "$buildDir\\libs\\lib"
from configurations.runtimeClasspath
into "$buildDir\\libs\\lib"
from configurations.compileClasspath
into "$buildDir\\libs\\lib"
}
task copyConfigFile(type: Copy) {
delete "$buildDir\\libs\\config"
from('src/main/resources')
into 'build/libs/config'
}
// copy configuration file
bootJar {
//archiveBaseName = 'application'
enabled = true
archiveVersion = '1.0.0'
// exclude all jars
excludes = ["*.jar"]
dependsOn copyJar
dependsOn copyConfigFile
manifest {
attributes "Manifest-Version": 1.0,
'Class-Path': configurations.runtimeClasspath.files.collect { "lib/$it.name" }.join(' ')
}
}
You should inject OnlineSessionService in constructor or by #Autowired as field rather than inject in method:
public AppSessionController(OnlineSessionService onlineSessionService){
this.onlineSessionService = onlineSessionService;
}
//Or like this
#Autowired
private OnlineSessionService onlineSessionService;
And I think the gradle config is wired, maybe use org.springframework.boot plugin to generate jar file is better than your tasks, please reference https://start.spring.io/ for more detail.

lombok.configuration apply #Generated annotation in multi-module project

We have a multi-module gradle project which runs sonarqube analysis. Our models annotated with lombok constructors/getters/setters/etc. bring our code coverage score down. I tried to add a lombok.config file with the following property:
lombok.addLombokGeneratedAnnotation = true
This was first added to the parent project, then moved to each child, then to each child's /src directory. However, the code coverage remains the same, and sonarqube still highlights the lombok Data/Getter/Setter/NoArgsConstructor/AllArgsConstructor/etc. annotations.
Furthermore, when inspecting the compiled classes, the Getter/Setter/equals/etc methods do not include the intended "Generated" annotation.
Our Parent project build.gradle file calls the plugins as:
plugins {
id "jacoco"
id "org.sonarqube" version "2.7"
}
...
jacocoTestReport {
reports {
xml.enabled true
}
}
sonarqube {
properties {
property 'sonar.login', <pwd>
property 'sonar.host.url', <host>
property 'sonar.projectKey', <key>
property 'sonar.java.coveragePlugin', 'jacoco'
property 'sonar.jacoco.reportPath', 'build/jacoco/test.exec'
property 'sonar.test.exclusions', '**/src/test/java/**/*'
property 'sonar.projectName', <name>
}
}
and lombok as:
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
The child modules generally have lombok dependencies of:
compileOnly 'org.projectlombok:lombok:edge-SNAPSHOT'
annotationProcessor 'org.projectlombok:lombok:edge-SNAPSHOT'
Furthermore, running gradle dependencies in a given module shows:
annotationProcessor - Annotation processors and their dependencies for source set 'main'.
\--- org.projectlombok:lombok -> 1.18.12
Is there something that we are missing?
when inspecting the compiled classes, the Getter/Setter/equals/etc methods do not include the intended "Generated" annotation
in absence of Minimal Reproducible Complete Example let me provide one:
given
lombok.config
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
settings.gradle
rootProject.name = 'basic-multiproject'
include 'module'
module/src/main/java/Example.java
#lombok.Data
#lombok.NoArgsConstructor
public class Example {
private String msg;
public static void uncovered() {
}
}
module/src/test/java/ExampleTest.java
import org.junit.Test;
public class ExampleTest {
#Test
public void test() {
new Example();
}
}
and module/build.gradle
plugins {
id "java"
id "jacoco"
}
repositories {
mavenCentral()
}
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'
testImplementation 'junit:junit:4.12'
}
jacoco.toolVersion = "0.8.7"
using Gradle 6.8.3
execution of
gradle build jacocoTestReport
produces module/build/classes/java/main/Example.class
that according to command
javap -v -p module/build/classes/java/main/Example.class
contains lombok.Generated annotations:
public java.lang.String getMsg();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field msg:Ljava/lang/String;
4: areturn
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LExample;
RuntimeInvisibleAnnotations:
0: #22()
lombok.Generated
and generates JaCoCo report in module/build/reports/jacoco/test/html/index.html
that doesn't show methods with these annotations:

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.

Kotlin 1.3 + Spring Boot: There is already '...' bean method

I'm trying to migrate a Spring Boot project from Kotlin 1.2.71 to 1.3.0.
When I update the Kotlin version, the application context fails to load with the following stack trace:
[...]
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'universityController' method
public final com.vindedu.api.view.university.UniversityApplicationOverview com.vindedu.api.controller.UniversityController.getApplications(org.springframework.security.core.userdetails.UserDetails,boolean)
to {[/uni/applications],methods=[GET]}: There is already 'universityController' bean method
public static com.vindedu.api.view.university.UniversityApplicationOverview com.vindedu.api.controller.UniversityController.getApplications$default(com.vindedu.api.controller.UniversityController,org.springframework.security.core.userdetails.UserDetails,boolean,int,java.lang.Object) mapped.
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:540)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:264)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:250)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:214)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:184)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 98 more
The following functions in my UniversityController class map to /uni/applications/...:
#ApiOperation("Get applications received from students", tags = ["University API"],
authorizations = [Authorization(value = "basicAuth")])
#RequestMapping(method = [RequestMethod.GET], path = ["/uni/applications"])
fun getApplications(#AuthenticationPrincipal user: UserDetails, #RequestParam("visible") visible: Boolean = true): UniversityApplicationOverview {
return universityMessagingService.getApplications(user, visible)
}
#ApiOperation("Toggle application visibility for university", tags = ["University API"],
authorizations = [Authorization(value = "basicAuth")])
#RequestMapping(method = [RequestMethod.PUT], path = ["/uni/applications/{id}/visible"])
fun toggleApplicationVisibility(#AuthenticationPrincipal user: UserDetails,
#PathVariable id: Long): UniversityApplicationDetails {
return universityMessagingService.toggleApplicationVisibility(user, id)
}
#ApiOperation("Get application received from student", tags = ["University API"],
authorizations = [Authorization(value = "basicAuth")])
#RequestMapping(method = [RequestMethod.GET], path = ["/uni/applications/{id}"])
fun getApplication(#AuthenticationPrincipal user: UserDetails,
#PathVariable id: Long): UniversityApplicationDetails {
return universityMessagingService.getApplication(user, id)
}
#ApiOperation("Update application status", tags = ["University API"],
authorizations = [Authorization(value = "basicAuth")])
#RequestMapping(method = [RequestMethod.PUT], path = ["/uni/applications/{id}/status"])
fun updateApplicationStatus(#AuthenticationPrincipal user: UserDetails,
#PathVariable id: Long,
#Valid #RequestBody updatedStatus: ApplicationStatusUpdate): UniversityApplicationDetails {
return universityMessagingService.updateApplicationStatus(user, id, updatedStatus)
}
When I deleted my mappings listed above, I saw the same stack trace at runtime for another controller.
This project runs flawless with Kotlin 1.2.71. I appreciate any suggestions very much to make it work with Kotlin 1.3.0! My full build.gradle is listed below.
Please note:
I compile and run this project with the following JDK:
java version "11" 2018-09-25 Java(TM) SE Runtime Environment 18.9
(build 11+28) Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28,
mixed mode)
I can't update Spring Boot yet, due to classes in the project that depend on Spring Boot 1.x.
build.gradle:
buildscript {
ext.kotlin_version = '1.3.0' // Was '1.2.71'
ext.spring_boot_version = '1.5.4.RELEASE'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Required for Kotlin integration
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
}
}
apply plugin: 'idea'
apply plugin: 'kotlin'
apply plugin: 'kotlin-allopen'
apply plugin: 'kotlin-noarg'
apply plugin: 'org.springframework.boot'
apply plugin: 'application'
allOpen {
annotation("org.springframework.boot.autoconfigure.SpringBootApplication")
annotation("org.springframework.stereotype.Service")
annotation("org.springframework.context.annotation.Configuration")
}
noArg {
annotation("javax.persistence.Entity")
}
jar {
baseName = 'vindedu-api'
version = '0.0.1'
}
repositories {
jcenter()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // Required for Kotlin integration
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" // Required for Kotlin integration
compile 'com.fasterxml.jackson.module:jackson-module-kotlin:2.9.2'
compile 'org.springframework.boot:spring-boot-starter-security'
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile 'org.springframework.boot:spring-boot-starter-actuator'
compile 'org.springframework.session:spring-session:1.3.1.RELEASE'
compile 'io.springfox:springfox-swagger2:2.7.0'
compile 'io.springfox:springfox-swagger-ui:2.7.0'
compile 'org.postgresql:postgresql:42.1.4'
compile 'org.flywaydb:flyway-core:4.2.0'
// JAXB dependencies don't ship with the JDK anymore
compile 'javax.xml.bind:jaxb-api:2.3.1'
compile 'com.sun.xml.bind:jaxb-impl:2.3.1'
compile 'javax.activation:activation:1.1.1'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'com.h2database:h2:1.4.196'
}
task wrapper(type: Wrapper) {
gradleVersion = '4.10.2'
}
springBoot {
mainClass = 'com.vindedu.api.ApplicationKt'
}
bootRun {
systemProperty("PROP_NAME", "prop-value")
}
test {
maxParallelForks = Runtime.runtime.availableProcessors() / 3
}
From 1.3.0 Kotlin compiler doesn't generate bridge flag for default methods and new bytecode are only supported in newer Spring Boot versions. Please upgrade to Spring Boot 2. Reference issue: https://youtrack.jetbrains.com/issue/KT-27947

Spring boot executable jar can not resolve freemarker template

I am learning web application building with spring boot and java. I've got my app working when I run it through Spring Tool Suite but after I build executable jar using bootRepackage and run it, It's not able to resolve the freemarker views.
I am not sure what's wrong. Any help would be appreciated.
Following is my application.properties related to freemarker,
spring.http.encoding.charset=UTF-8
spring.freemarker.cache=false
spring.freemarker.charset=utf-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.enabled=true
spring.freemarker.suffix=.html
spring.freemarker.template-loader-path=classpath:/templates/,classpath:/templates/web/
My jar structure,
BOOT-INF
classes
com
scss
static
templates
web
story.html
app
application.properties
log4j2.xml
META-INF
org
my controller,
#Controller
public class HomeController {
#Autowired
private AppLog appLogger;
#RequestMapping("/")
public ModelAndView Index(HttpServletRequest request) {
appLogger.log(Level.ERROR,AppLogSource.Web, "Reached Controller", null);
String testAttribute = request.getAttribute("com.demo.test").toString();
Map<String, String> vm = new HashMap<String, String>();
vm.put("testAttribute", testAttribute);
return new ModelAndView("/web/story", vm);
}
}
I verified that I am hitting the log step so I think issue is in resolving the view but I could be wrong and missing something else. So let me know if you need more info.
Thanks again!
Best,
Mrunal
edit
Gradle File,
buildscript {
ext {
springBootVersion = '1.4.1.RELEASE'
}
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("com.moowork.gradle:gradle-node-plugin:1.2.0")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
apply plugin: 'com.moowork.node'
apply plugin: 'com.moowork.grunt'
jar {
baseName = 'testDemo'
version = '0.0.1'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
node {
version = '6.11.3'
npmVersion = '3.10.10'
download = true
}
task gruntMinifyJs(type: GruntTask){
args=['minifyJs', '--debug']
}
task gruntMinifyCss(type: GruntTask){
args=['minifyCss', '--debug']
}
task buildFrontEnd(type: GruntTask) {
args = ['default', '--debug']
}
npmInstall.dependsOn(nodeSetup)
buildFrontEnd.dependsOn(npmInstall)
gruntMinifyCss.dependsOn(npmInstall)
gruntMinifyJs.dependsOn(npmInstall)
build.dependsOn(buildFrontEnd)
configurations {
all*.exclude group: 'ch.qos.logback', module:'logback-classic'
all*.exclude group: 'ch.qos.logback', module:'logback-core'
}
dependencies {
compile('org.springframework.boot:spring-boot-devtools')
compile('org.springframework.boot:spring-boot-starter-freemarker')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter:1.4.1.RELEASE'){
exclude group:'org.springframework.boot', module:'spring-boot-starter-logging'
}
compile('org.springframework.boot:spring-boot-starter-jdbc'){
exclude group:'org.apache.tomcat', module:'tomcat-jdbc'
}
compile('mysql:mysql-connector-java')
compile('com.zaxxer:HikariCP-java6:2.3.13')
compile('org.springframework.boot:spring-boot-starter-log4j2:1.4.1.RELEASE')
compile('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.8')
compile('com.google.zxing:core:3.3.0')
compile('org.antlr:antlr4-runtime:4.5')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
edit 3,
further updates,
So I attached remote debugger and I found that spring is using ContentNegotiatingViewResolver to resolve the view as InternalResourceView but when I execute through spring tool suite it resolves properly to FreemarkerView.
I hope this helps someone to narrow down my issue. I'll see if I can get anywhere else in mean time by stepping through debugger.
Perhaps the freemarker jar file is not specified as a dependency in your spring boot application module. Unsure if you are running maven or gradle but do make sure you have the freemarker library included in your build.
See downloads # https://mvnrepository.com/artifact/org.freemarker/freemarker

Resources