I have no idea what's happening here, there shouldn't be a connection. Not one that manifests this specifically, anyways.
After moving the buildscript of a major spring-boot project from groovy to kotlin-DSL, some unit tests are throwing NullPointerException. It's all the unit tests that deal with methods that have manual transaction management using a TransactionTemplate. This has not happened before the move, and by now I have compared the old and the new buildscript a dozen times over and am pretty confident that I didn't forget anything.
Let's start out by comparing the two versions of the build scripts.
Old groovy:
buildscript {
ext {
kotlinVersion = '1.4.10'
springBootVersion = '2.3.3.RELEASE'
awsSdkVersion = '1.11.381'
springfoxVersion = '2.9.2'
kotlintestVersion = '3.1.7'
}
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion"
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlinVersion"
// liquibase stuff
classpath 'mysql:mysql-connector-java:5.1.44'
classpath 'org.yaml:snakeyaml:1.19'
classpath('org.liquibase:liquibase-gradle-plugin:1.2.3') {
exclude group: "org.liquibase", module: "liquibase-core"
}
classpath "org.liquibase:liquibase-core:3.5.3" // should be in sync with the version included in spring boot
}
}
plugins {
id 'net.researchgate.release' version '2.6.0'
}
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'kotlin-jpa'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'jacoco'
apply plugin: 'org.liquibase.gradle'
apply plugin: 'idea'
group = 'webcam.yellow.service'
sourceCompatibility = 1.8
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
jacocoTestReport {
reports {
xml.enabled = true
html.enabled = true
}
}
check.dependsOn jacocoTestReport
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven { url = "http://clojars.org/repo/" }
maven {
credentials {
username "${mavenUser}"
password "${mavenPassword}"
}
url = "https://artifactory.yellow.webcam/artifactory/releases"
}
maven {
credentials {
username "${mavenUser}"
password "${mavenPassword}"
}
url = "https://artifactory.yellow.webcam/artifactory/snapshots"
}
}
idea {
module {
// if you hate browsing Javadoc
downloadJavadoc = false
// and love reading sources :)
downloadSources = true
}
}
dependencies {
// avisec libraries
compile "webcam.yellow.api:webcam-api:3.2.0"
compile "webcam.yellow.authentication:messaging-authentication:1.5"
// Spring Boot
compile 'org.springframework.boot:spring-boot-starter-actuator'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-security'
compile 'org.springframework.boot:spring-boot-starter-activemq'
compile 'org.springframework.boot:spring-boot-starter-validation'
compile 'io.micrometer:micrometer-registry-influx'
compile 'com.fasterxml.jackson.module:jackson-module-kotlin'
compile 'com.fasterxml.jackson.datatype:jackson-datatype-joda'
// Joda
compile 'joda-time:joda-time'
compile 'org.jadira.usertype:usertype.core:6.0.1.GA'
// Libraries
compile group: 'org.apache.commons', name: 'commons-text', version: '1.1'
compile group: 'org.apache.commons', name: 'commons-csv', version: '1.8'
// Kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}"
compile "org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}"
// Mail
compile group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '2.2.4.RELEASE'
compile group: 'org.apache.commons', name: 'commons-email', version: '1.5'
compile group: 'javax.mail', name: 'mail', version: '1.5.0-b01'
// AWS
compile "com.amazonaws:aws-java-sdk-s3:$awsSdkVersion"
compile group: 'com.amazonaws', name: 'aws-java-sdk-sqs', version: '1.11.558'
compile group: 'software.amazon.awssdk', name: 's3', version: '2.10.61'
compile group: 'com.amazonaws', name: 'aws-java-sdk-ec2', version: '1.11.740'
// Support old pw hash lib
compile group: 'buddy', name: 'buddy-hashers', version: '1.3.0'
// Hazelcast
compile group: 'com.hazelcast', name: 'hazelcast-spring'
compile group: 'com.hazelcast', name: 'hazelcast-aws', version: '2.4'
// Swagger
compile group: 'io.springfox', name: 'springfox-swagger2', version: springfoxVersion
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: springfoxVersion
// Database
runtime 'mysql:mysql-connector-java'
runtime 'org.liquibase:liquibase-core'
compile 'org.influxdb:influxdb-java:2.13'
// HTML Generation
compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-html-jvm', version: '0.6.10'
// ActiveMQ
compile group: 'org.messaginghub', name: 'pooled-jms'
// Development
compile "org.springframework.boot:spring-boot-devtools"
// Testing
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'org.springframework.security:spring-security-test'
testCompile 'com.h2database:h2'
testCompile 'io.kotlintest:kotlintest:2.0.7'
testCompile 'com.nhaarman:mockito-kotlin:1.6.0'
// GSON
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
}
liquibase {
activities {
main {
changeLogFile '/db/changelog/db.changelog-master.yaml'
url 'jdbc:mysql://localhost:3307/yellow'
username 'user'
password 'secret'
classpath 'src/main/resources'
}
}
}
springBoot {
buildInfo()
}
task ebextensions(type: Exec) {
executable "sh"
args "-c", "jar uf build/libs/webcam-service*.jar .ebextensions"
}
bootJar.finalizedBy ebextensions
afterReleaseBuild.dependsOn bootJar
test {
minHeapSize = "1024m"
maxHeapSize = "1024m"
jvmArgs = ["-Xloggc:build/gclog-%p.log", "-XX:+PrintGCDetails"]
}
And the new kotlin one:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
buildscript {
dependencies {
classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4.9.6")
classpath("org.jetbrains.kotlin:kotlin-allopen:1.4.10")
// liquibase stuff
classpath("mysql:mysql-connector-java:5.1.44")
classpath("org.yaml:snakeyaml:1.19")
classpath("org.liquibase:liquibase-gradle-plugin:1.2.3") {
exclude("org.liquibase", "liquibase-core")
}
classpath("org.liquibase:liquibase-core:3.8.9") // should be in sync with the version included in spring boot
}
}
val awsSdkVersion = "1.11.740"
val springfoxVersion = "2.9.2"
plugins {
id("org.springframework.boot") version "2.3.3.RELEASE"
id("io.spring.dependency-management") version "1.0.9.RELEASE"
id("net.researchgate.release") version "2.6.0"
kotlin("jvm") version "1.4.10"
kotlin("plugin.spring") version "1.4.10"
`maven-publish`
id("com.jfrog.artifactory") version "4.14.1"
id("org.liquibase.gradle") version "2.0.4"
id("org.jetbrains.kotlin.plugin.noarg") version "1.4.10"
id("org.jetbrains.kotlin.plugin.jpa") version "1.4.10"
}
group = "webcam.yellow.service"
java.sourceCompatibility = JavaVersion.VERSION_1_8
val mavenUser: String by project
val mavenPassword: String by project
val artifactoryRepository = System.getenv("ARTIFACTORY_REPO") ?: "snapshots"
repositories {
mavenCentral()
jcenter()
maven {
credentials {
username = mavenUser
password = mavenPassword
}
url = uri("https://artifactory.yellow.webcam/artifactory/releases")
}
maven {
credentials {
username = mavenUser
password = mavenPassword
}
url = uri("https://artifactory.yellow.webcam/artifactory/snapshots")
}
}
dependencies {
// Spring Boot
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-activemq")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("io.micrometer:micrometer-registry-influx")
// Kotlin
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8")
// Yellow Dependencies
implementation("webcam.yellow.api:webcam-api:3.2.0")
implementation("webcam.yellow.authentication:messaging-authentication:1.5")
// AWS
implementation("com.amazonaws:aws-java-sdk-s3:$awsSdkVersion")
implementation("software.amazon.awssdk:s3:2.10.61") // does not yet incorporate all functionality of 1.11.X
implementation("com.amazonaws:aws-java-sdk-sqs:$awsSdkVersion")
implementation("com.amazonaws:aws-java-sdk-ec2:$awsSdkVersion")
// Support old pw hash lib
implementation("buddy:buddy-hashers:1.3.0")
// Jackson
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda")
// Hazelcast
implementation("com.hazelcast:hazelcast-spring")
implementation("com.hazelcast:hazelcast-aws:2.4")
// Other Libs
implementation("org.apache.commons:commons-text:1.1")
implementation("org.apache.commons:commons-csv:1.8")
implementation("org.messaginghub:pooled-jms")
implementation("org.influxdb:influxdb-java:2.13")
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.10")
implementation("org.springframework.boot:spring-boot-devtools")
implementation("com.google.code.gson:gson:2.8.6")
implementation("joda-time:joda-time")
implementation("org.jadira.usertype:usertype.core:6.0.1.GA")
// Swagger
implementation("io.springfox:springfox-swagger2:$springfoxVersion")
implementation("io.springfox:springfox-swagger-ui:$springfoxVersion")
// Database
runtimeOnly("mysql:mysql-connector-java")
runtimeOnly("org.liquibase:liquibase-core")
implementation("org.influxdb:influxdb-java:2.13")
// Test
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("com.h2database:h2")
testImplementation("com.nhaarman:mockito-kotlin:1.6.0")
testImplementation("io.kotlintest:kotlintest:2.0.7")
}
tasks.withType<Test> {
useJUnitPlatform()
}
springBoot {
buildInfo()
}
liquibase {
activities.register("main") {
arguments = mapOf(
"changeLogFile" to "/db/changelog/db.changelog-master.yaml",
"url" to "jdbc:mysql://localhost:3307/yellow",
"username" to "user",
"password" to "secret",
"classpath" to "src/main/resources"
)
}
}
tasks {
withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
bootJar {
from("./.ebextensions") { into(".ebextensions") }
launchScript()
}
afterReleaseBuild {
dependsOn(bootJar)
}
test {
minHeapSize = "1024m"
maxHeapSize = "1024m"
jvmArgs = listOf("-Xloggc:build/gclog-%p.log", "-XX:+PrintGCDetails")
}
}
Some things needed different handling, like moving some plugins from the classpath to the plugins section, the .ebsextensions are handled differently, and Jacoco isn't in there anymore because we never really used those reports in jenkins. All in all, pretty normal. Everything works, except for this one thing in the unit tests.
Here's a class initialisation and one of the tested methods:
#Service
class ImageService(private val imageRepository: ImageRepository,
private val imageSetRepository: ImageSetRepository,
private val permissionService: PermissionService,
private val s3Service: S3Service,
private val entityManager: EntityManager,
private val panoFeedRepository: PanoFeedRepository,
private val panoImageRepository: PanoImageRepository,
private val jobFacade: JobFacade,
private val jdbcTemplate: NamedParameterJdbcTemplate,
transactionManager: PlatformTransactionManager,
#Value("\${service.cleanup.enabled}")
private val cleanupEnabled: Boolean) {
private val readTransaction = org.springframework.transaction.support.TransactionTemplate(
transactionManager,
DefaultTransactionDefinition().apply {
isReadOnly = true
propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW
})
private val writeTransaction = org.springframework.transaction.support.TransactionTemplate(
transactionManager,
DefaultTransactionDefinition().apply {
isReadOnly = false
propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW
})
/**
* Deletes all passed images from the database while removing them from S3 asynchronously.
* Does not check any permissions.
* This opens its own write transaction, which could take a long time. If individual transactions take too long,
* invoke this method multiple times with smaller chunks to avoid issues with table locking.
* Since this is potentially long-running, commit preceding transactions beforehand.
*/
#Transactional(propagation = Propagation.NEVER)
internal fun delete(images: List<ImageIdAndImageKeyAndImagePreviewKey>) {
images.chunked(500).forEach { chunk ->
log.debug("Deleting s3 files of {} images", chunk.size)
jobFacade.executeAsync {
val keysToDelete = chunk
.flatMap { listOf(it.getImageKey(), it.getImagePreviewKey()) }
.asSequence()
.filterNotNull()
.filter { !it.isBlank() }
.toList()
s3Service.deleteImages(keysToDelete)
log.trace("deleted {} s3 files of {} images", keysToDelete.size, chunk.size)
}
log.debug("Deleting {} images from database", chunk.size)
writeTransaction.execute {
imageRepository.deleteAllByIdIn(chunk.map { it.getId()!! })
} // <---- Exception happens in here
log.trace("Deleted {} images from database", chunk.size)
}
}
}
And here's a test setup and a test that tests tat method:
class ImageServiceTest {
private val imageRepository: ImageRepository = mock()
private val imageSetRepository: ImageSetRepository = mock()
private val permissionService: PermissionService = mock()
private val s3Service: S3Service = mock()
private val hazelCastInstance: HazelcastInstance = mock()
private val panoFeedRepository: PanoFeedRepository = mock()
private val panoImageRepository: PanoImageRepository = mock()
private val jdbcTemplate: NamedParameterJdbcTemplate = mock()
private val transactionManager: PlatformTransactionManager = mock()
private val jobFacade: JobFacade
private val sut: ImageService
init {
val imap: IMap<String, String> = mock()
`when`(hazelCastInstance.getMap<String, String>(any())).thenReturn(imap)
jobFacade = JobFacade(hazelCastInstance)
sut = ImageService(
imageRepository, imageSetRepository, permissionService, s3Service, mock(),
panoFeedRepository, panoImageRepository, jobFacade, jdbcTemplate, transactionManager, false)
}
#Test
fun `deleting multiple images invokes S3Service in chunks and deletes images from DB`() {
val user = TestModelFactory.userWithOneCameraAndTablePermission()
val table = user.tablePermissions.first().table!!
val images = (1..800).map { TestModelFactory.image(table = table) }
sut.delete(images.map { ImageIdentifiers(it.id, it.imageKey, it.imagePreviewKey) })
Thread.sleep(2000) //leave enough time for S3 uploads to finish
val idChunks = images.map { it.id }.chunked(500)
verify(imageRepository, times(1)).deleteAllByIdIn(eq(idChunks[0]))
verify(imageRepository, times(1)).deleteAllByIdIn(eq(idChunks[1]))
verify(s3Service, times(2)).deleteImages(any())
}
And finally, the error:
java.lang.NullPointerException: Parameter specified as non-null is null: method webcam.yellow.service.service.ImageService$delete$$inlined$forEach$lambda$2.doInTransaction, parameter it
Now, there's several things I'm currently looking into about what might go wrong, involving the mocked TransactionManager and the non-mocked TransactionTemplates... But the really frustrating thing is that this simply didn't happen before migrating the build script, and nothing else changed.
It turns out that the transactionTemplate (which is not mocked) invoked a method on the mock of the platformTransactionManager, which returned null (obviously), and that caused the NullPointerException.
It was fixed relatively easily by configuring the mock instance to return a value instead:
given(transactionManager.getTransaction(any())).willReturn(mock())
What I don't know is why this ever worked before. This problem should always have been there, but it wasn't... Only when I migrated the build file did it show its head, which doesn't really make much sense.
I am getting the following issue in Jenkins when updating gradle;
Execution failed for task ':xxx:extractModuleInfo'.
06:38:45 > Artifact xxxxx.jar wasn't produced by this build.
I have been researching on the issue and I think it's known issue when updating gradle.
Please refer to the section Publishing Spring Boot Applications in this link:
https://docs.gradle.org/current/userguide/upgrading_version_6.html
Here is the buildgradle of the application:
import java.time.format.DateTimeFormatter
import java.time.ZonedDateTime
apply plugin: 'org.springframework.boot'
apply plugin: 'war'
def checkpoint = version
description 'Spring Boot / MVC web application (controllers for SEAR services, etc)'
springBoot {
// Generates build info and will be used in /info endpoint
buildInfo()
}
/*
bootRun {
addResources = false
systemProperties = System.properties
main = 'creditcard.lifecycle.CreditCardApplication'
jvmArgs = [ "-Djavax.net.ssl.trustStore=..//cacerts"]
}
*/
dependencies {
implementation project(':xx')
implementation project(':xx')
implementation project(':xxxx')
implementation project(':xxxxxxxxxxxxxxxxxxxxxx')
implementation project(':xxxxxxxxxxxxxxxxxxxxxxx')
implementation project(':xxxxxxxxxxxxxxx')
implementation project(':xxxxxxxxxxxxxxxx')
implementation project(':xxx')
implementation project(':xxxxx)
implementation project(':xxxxx')
implementation project(':xxxxx')
implementation project(':xxxxx')
implementation project(':xxxxx')
implementation libraries.wf_retrofit
implementation libraries.commons_lang3
implementation ('org.springframework.boot:spring-boot-starter-web') {
exclude module: "spring-boot-starter-tomcat"
exclude module: "tomcat-embed-core"
exclude module: "tomcat-embed-el"
exclude module: "tomcat-embed-websocket"
exclude module: "tomcat-annotations-api"
}
implementation ('org.springframework.boot:spring-boot-starter-tomcat:1.5.8.RELEASE')
implementation ('wf.authx:authx:0.9+')
implementation libraries.jackson
implementation libraries.searj
implementation libraries.commonscodec
implementation ('org.springframework.boot:spring-boot-starter-actuator')
//include schema after it is built
//compile group: 'wf.ebs', name: 'schemas', version: '2018.1-SNAPSHOT'
runtimeOnly 'org.springframework.boot:spring-boot-starter-undertow'
}
configurations {
compile.exclude module: 'spring-boot-starter-tomcat'
compile.exclude group: 'org.apache.tomcat'
compile.exclude module: "tomcat-embed-el"
compile.exclude module: "searj-authx-spring-boot-starter"
}
task createCheckpointFile {
doLast {
//Need to pass variables as jenkins parameters
//def checkpoint = "xxxx_${Release}.${BUILD_NUMBER}"
println "Checkpoint = ${checkpoint}"
def TODAY_DT_US = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z"))
String fileContent = "BUILD_CHECKPOINT=${checkpoint}\nBUILD TSTAMP: ${TODAY_DT_US} \nBUILD_DIR=$projectDir"
new File("${buildDir}/xxxxx.checkpoint").write(fileContent)
}
}
/*
jar {
from file("${buildDir}/xxxx.checkpoint")
baseName = 'xxxxxx'
manifest {
attributes("Implementation-Title": "xxxx",
"Implementation-Version": "$project.version")
}
}
*/
//bootRepackage.enabled = false
//jar.dependsOn createCheckpointFile
war {
enabled = true
archiveFileName = 'xxxx.war'
//copy-move
webInf { from("${buildDir}/xxxx.checkpoint") }
}
war.dependsOn createCheckpointFile
How is that components feature adding that check to the jar?
Add/modify jar task as below in build.gradle
jar {
enabled = true
}
This should fix the issue
I'm trying to upgrade my existing Java 8 multi-project gradle application to Java 11. After fixing a few compilation issues, I ended up getting issues in test cases. When i run a test in Intellij, it throws the following error:
Error:java: Attempt to recreate a file for type {QueryDsl classes}
It is trying to generate the Querydsl classes but since those classes are already there, the test is throwing exception.
I'm using Java11, IntelliJ 2019, Gradle 5 to run the application.
These tests are working as expected in Java8.
I've no idea what is causing this error. Can anybody please help me in understanding this.
Code snippets are given below.
Root project build.gradle:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.4.RELEASE)
classpath("net.ltgt.gradle:gradle-apt-plugin:0.21")
}
}
subprojects {
apply plugin: 'java'
repositories {
mavenCentral()
}
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
task allDependencies(type: DependencyReportTask) {}
jar {
baseName = "${parent.name}-${project.name}"
}
sourceSets {
main {
java {
srcDirs 'src/main/java', 'build/generated/sources/main/java', 'build/generated/sources/annotationProcessor/java/main'
}
}
}
}
Sub-project build.gradle:
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'net.ltgt.apt'
bootJar {
baseName = "test"
version = "1.0.0"
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa: 2.1.4.RELEASE")
compile("com.querydsl:querydsl-core:4.1.3")
compile("com.querydsl:querydsl-jpa:4.1.3")
annotationProcessor(
"com.querydsl:querydsl-apt:4.1.3:jpa",
"javax.annotation:javax.annotation-api:1.3.2"
)
annotationProcessor("org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final")
testCompile("org.springframework.boot:spring-boot-starter-test:2.1.4.RELEASE")
testCompile("com.h2database:h2:2.1.4.RELEASE")
}
Integration test
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MainClass.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServiceImplTest {
#Autowired
private Service1Impl service;
#Test
public void getData() {
Data data = service.getData();
Assert.assertEquals(0, data.size());
}
}
I had the same issue and the problem was with the order of the dependencies in gradle. Somehow the java compiler in intellij can not work well.
Be sure you have this dependencies for QueryDSL
annotationProcessor group: 'com.querydsl', name: 'querydsl-apt', version: querydsl_version, classifier: 'jpa'
annotationProcessor group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.1-api', version: hibernate_jpa_version
annotationProcessor group: 'javax.annotation', name: 'javax.annotation-api', version: javax_annotation_version
Delete the out folder in your project just in case and rebuild with Ctrl+f9.
Note aside, executing build from gradle and test command worked fine. Check this out https://blog.jdriven.com/2018/10/using-querydsl-annotation-processor-with-gradle-and-intellij-idea/
I'm trying to setup Gatling with Spring Boot 2.0.1.RELEASE.
I've created this api:
#RestController
#RequestMapping("/contact")
public class ContactController {
#GetMapping
public ResponseEntity get() {
return ResponseEntity.ok("Contact retrieved successfully");
}
}
And this basic simulation:
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class ContactSimulation extends Simulation {
val httpConf = http.baseURL("http://localhost:8080")
val scn = scenario("GetContact")
.exec(
http("GetContact")
.get("/contact")
.check(status.is(200))
)
setUp(scn.inject(atOnceUsers(1))).protocols(httpConf)
}
The build.gradle is configured this way:
buildscript {
ext {
springBootVersion = '2.0.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'scala'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.gatling.poc'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('io.gatling.highcharts:gatling-charts-highcharts:2.3.0')
}
task loadTest(type: JavaExec) {
dependsOn testClasses
description = "Load Test With Gatling"
group = "Load Test"
classpath = sourceSets.test.runtimeClasspath
jvmArgs = [
"-Dgatling.core.directory.binaries=${sourceSets.test.output.classesDir.toString()}"
]
main = "io.gatling.app.Gatling"
args = [
"--simulation", "com.gatling.poc.simulations.ContactSimulation",
"--results-folder", "${buildDir}/gatling-results",
"--binaries-folder", sourceSets.test.output.classesDir.toString(),
"--bodies-folder", sourceSets.test.resources.srcDirs.toList().first().toString() + "/gatling/bodies",
]
}
While executing the tests:
./gradlew loadTest
I'm getting the following stacktrace:
21:00:12.804 [main] WARN io.netty.util.concurrent.DefaultPromise - An exception was thrown by org.asynchttpclient.netty.request.NettyRequestSender$1.operationComplete()
java.lang.NoSuchMethodError: io.netty.channel.DefaultChannelId.newInstance()Lio/netty/channel/DefaultChannelId;
at io.netty.channel.AbstractChannel.newId(AbstractChannel.java:111)
at io.netty.channel.AbstractChannel.<init>(AbstractChannel.java:83)
at io.netty.bootstrap.FailedChannel.<init>(FailedChannel.java:33)
at io.netty.bootstrap.AbstractBootstrap.initAndRegister(AbstractBootstrap.java:330)
at io.netty.bootstrap.Bootstrap.doResolveAndConnect(Bootstrap.java:163)
at io.netty.bootstrap.Bootstrap.connect(Bootstrap.java:156)
at org.asynchttpclient.netty.request.NettyChannelConnector.connect0(NettyChannelConnector.java:81)
at org.asynchttpclient.netty.request.NettyChannelConnector.connect(NettyChannelConnector.java:69)
at org.asynchttpclient.netty.request.NettyRequestSender$1.onSuccess(NettyRequestSender.java:292)
at org.asynchttpclient.netty.request.NettyRequestSender$1.onSuccess(NettyRequestSender.java:285)
at org.asynchttpclient.netty.SimpleFutureListener.operationComplete(SimpleFutureListener.java:24)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:511)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:485)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:424)
at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:162)
at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:33)
at org.asynchttpclient.netty.request.NettyRequestSender.sendRequestWithNewChannel(NettyRequestSender.java:285)
at org.asynchttpclient.netty.request.NettyRequestSender.sendRequestWithCertainForceConnect(NettyRequestSender.java:136)
at org.asynchttpclient.netty.request.NettyRequestSender.sendRequest(NettyRequestSender.java:107)
at org.asynchttpclient.DefaultAsyncHttpClient.execute(DefaultAsyncHttpClient.java:216)
at org.asynchttpclient.DefaultAsyncHttpClient.executeRequest(DefaultAsyncHttpClient.java:184)
at org.asynchttpclient.DefaultAsyncHttpClient.executeRequest(DefaultAsyncHttpClient.java:206)
at io.gatling.http.ahc.HttpEngine.warmpUp(HttpEngine.scala:96)
at io.gatling.http.protocol.HttpProtocol$$anon$1.$anonfun$newComponents$1(HttpProtocol.scala:62)
at io.gatling.core.protocol.ProtocolComponentsRegistry.comps$1(Protocol.scala:67)
at io.gatling.core.protocol.ProtocolComponentsRegistry.$anonfun$components$4(Protocol.scala:69)
at scala.collection.mutable.HashMap.getOrElseUpdate(HashMap.scala:82)
at io.gatling.core.protocol.ProtocolComponentsRegistry.components(Protocol.scala:69)
at io.gatling.http.action.HttpActionBuilder.lookUpHttpComponents(HttpActionBuilder.scala:25)
at io.gatling.http.action.sync.HttpRequestActionBuilder.build(HttpRequestActionBuilder.scala:33)
at io.gatling.core.structure.StructureBuilder.$anonfun$build$1(StructureBuilder.scala:34)
at scala.collection.LinearSeqOptimized.foldLeft(LinearSeqOptimized.scala:122)
at scala.collection.LinearSeqOptimized.foldLeft$(LinearSeqOptimized.scala:118)
at scala.collection.immutable.List.foldLeft(List.scala:86)
at io.gatling.core.structure.StructureBuilder.build(StructureBuilder.scala:33)
at io.gatling.core.structure.StructureBuilder.build$(StructureBuilder.scala:32)
at io.gatling.core.structure.ScenarioBuilder.build(ScenarioBuilder.scala:38)
at io.gatling.core.structure.PopulationBuilder.build(ScenarioBuilder.scala:98)
at io.gatling.core.scenario.SimulationParams.$anonfun$scenarios$1(Simulation.scala:188)
at scala.collection.immutable.List.map(List.scala:283)
at io.gatling.core.scenario.SimulationParams.scenarios(Simulation.scala:188)
at io.gatling.app.Runner.run0(Runner.scala:95)
at io.gatling.app.Runner.run(Runner.scala:64)
at io.gatling.app.Gatling$.start(Gatling.scala:59)
at io.gatling.app.Gatling$.fromArgs(Gatling.scala:43)
at io.gatling.app.Gatling$.main(Gatling.scala:35)
at io.gatling.app.Gatling.main(Gatling.scala)
If I create the same project using SpringBoot 1.5.12.RELEASE, it works perfectly. I'm suspecting of the spring-boot-gradle-plugin since it changes according to the SpringBoot version.
If necessary I can create a project into github to demonstrate the problem. =)
I would appreciate if someone could help me!
I got fix this issue.
The problem was the version of the io.netty group used by the SpringBoot 2.0.1.RELEASE. It is using the version 4.1.23.Final which for some reason (maybe some change in scala) is not executing the gatling tests.
I did the downgrade to use the same version as SpringBoot 1.5.12.RELEASE, which is 4.0.51.Final. I had to add this configuration to the build.gradle:
{
// This build.gradle is the same that I posted above. For the sake of simplicity I just put the configuration that has been necessary to solve the problem.
// After dependencies{} section, I had to put this:
configurations.all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'io.netty') {
details.useVersion "4.0.51.Final"
}
}
}
}
}
I don't think is the best solution since I'm reverting the io.netty version to use a older version, which can have some side effect. In my case I didn't have the option of downgrading the SpringBoot version. If you have the option to revert the SpringBoot version, I would recommend to do that.
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