I have been having issues with spring boot tests for loading context with spring boot 2.6.6 and above. Note: I do have META-INF file for JSR setup in resources.
Tests fails to load JSR engine.
Here is my test app
#SpringBootApplication
class JsrErrorApplication{
#Bean
fun scriptingEngine(): ScriptEngine = ScriptEngineManager().getEngineByExtension("kts")
}
#Component
class JService(private val scriptEngine: ScriptEngine){
init {
println(scriptEngine.eval("2+2"))
}
}
fun main(args: Array<String>) {
runApplication<JsrErrorApplication>(*args)
}
And depdenecies:
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-scripting-jsr223:1.6.21")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
When I start the app it runs fine with 2.7.0 but my test fail to load the context
#SpringBootTest
class JsrErrorApplicationTests {
#Test
fun contextLoads() {
}
}
Here is exception when I run the test
Caused by: java.lang.ClassNotFoundException: kotlin.Array
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.parseType(KDeclarationContainerImpl.kt:265)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.loadParameterTypes(KDeclarationContainerImpl.kt:256)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findMethodBySignature(KDeclarationContainerImpl.kt:197)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:68)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:61)
at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:63)
at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:61)
at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63)
at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:136)
at org.springframework.core.MethodParameter$KotlinDelegate.getGenericReturnType(MethodParameter.java:915)
at org.springframework.core.MethodParameter$KotlinDelegate.access$000(MethodParameter.java:866)
at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:510)
at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:291)
at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:107)
at org.springframework.core.ResolvableType.forType(ResolvableType.java:1421)
at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1342)
at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1324)
at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1291)
at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1251)
at org.springframework.core.GenericTypeResolver.resolveReturnType(GenericTypeResolver.java:80)
at org.springframework.beans.GenericTypeAwarePropertyDescriptor.<init>(GenericTypeAwarePropertyDescriptor.java:110)
at org.springframework.beans.CachedIntrospectionResults.buildGenericTypeAwarePropertyDescriptor(CachedIntrospectionResults.java:431)
at org.springframework.beans.CachedIntrospectionResults.introspectInterfaces(CachedIntrospectionResults.java:349)
at org.springframework.beans.CachedIntrospectionResults.<init>(CachedIntrospectionResults.java:322)
at org.springframework.beans.CachedIntrospectionResults.forClass(CachedIntrospectionResults.java:183)
at org.springframework.beans.BeanWrapperImpl.getCachedIntrospectionResults(BeanWrapperImpl.java:174)
at org.springframework.beans.BeanWrapperImpl.getPropertyDescriptors(BeanWrapperImpl.java:248)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.filterPropertyDescriptorsForDependencyCheck(AbstractAutowireCapableBeanFactory.java:1594)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.filterPropertyDescriptorsForDependencyCheck(AbstractAutowireCapableBeanFactory.java:1574)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1434)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
... 114 more
JsrErrorApplicationTests > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:628
Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:641
1 test completed, 1 failed
However when the application is started everything is found correectly:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.0)
2022-06-07 16:31:11.187 INFO 8396 --- [ main] c.e.jsrerror.JsrErrorApplicationKt : Starting JsrErrorApplicationKt using Java 17.0.1 on Desktop with PID 8396 (C:\jsrError\build\classes\kotlin\main started by *** in C:\Learning\Kotlin\jsrError)
2022-06-07 16:31:11.196 INFO 8396 --- [ main] c.e.jsrerror.JsrErrorApplicationKt : No active profile set, falling back to 1 default profile: "default"
4
2022-06-07 16:31:13.490 INFO 8396 --- [ main] c.e.jsrerror.JsrErrorApplicationKt : Started JsrErrorApplicationKt in 2.539 seconds (JVM running for 2.88)
Process finished with exit code 0
Same context loads fine on spring boot 2.6.5, do I need to change something?
Related
I have a problem with a #SpringBootTest integration test that is driving me nuts, I hope there are some Spring gurus around that can shed some light on this issue:
Our integration tests need to have a fully initialized ApplicationContext so we dont run them with any special test profiles. This worked well in all environments. I've just now updated to Spring Boot 2.4.3 (from 2.3.4) and experience a really weird problem:
When I run the test locally (either from the IDE or using mvn test) it just runs as expected: As I have no profile to run specified whatsoever he's picking up the default default profile as it is also shown on the INFO log output when starting:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.3)
2021-02-25 14:24:29.920 INFO [service-api] [ main] d.r.i.i.s.a.ServiceApiApplicationTest : No active profile set, falling back to default profiles: default
2021-02-25 14:24:31.599 INFO [service-api] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
...
If the same thing is run in our CI pipeline using mvn test (well actually it is Gitlab AutoDevOps which utilizes the herokuish java buildpack but i'm pretty sure it boils down to mvn test) he does not use the default default profile which is causing problems afterwards:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.3)
2021-03-10 15:31:03.925 INFO [service-api] [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888
2021-03-10 15:31:04.034 INFO [service-api] [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://localhost:8888. Will be trying the next url if available
...
(You see the problem: we're using Spring Cloud config for every environment except default but this is just a follow-up problem, the root cause is that when running in Gitlab CI it just does not use the default profile.)
So the question is: What part of the environment makes the magic Spring Boot Context initialization process not going through the standard org.springframework.boot.SpringApplication#logStartupProfileInfo code its supposed to go every time (like it is documented in the official documentation:
The annotation works by creating the ApplicationContext used in your tests through SpringApplication.
?
This problem just occurs after updating to Spring Boot 2.4.3, with v2.3.4 it worked in Gitlab CI just as fine as locally.
(Oh and i know i can würg-around this issue by specifying the profile on the test using #ActiveProfiles("default") but dont you agree that this is horrible?)
I have an extremely basic SpringBootTest that I am trying to run (the contextLoads()) test.
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
#SpringBootTest
#ActiveProfiles("test")
public class ApplicationTest {
#Test
void contextLoads() {
}
}
I have a config file application-test.yml under src/test/resources/ with some basic configuration that I'd like to use to test my application with. However even with the simple test above the context isn't loading. I'm using IntelliJ to run my tests - and when I run the unit test with Maven the test just is in a running state but it seems like it's frozen loading the context. The only thing I see is
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.1.RELEASE)
and that it says "Running tests..." and it doesn't resolve. I'm not exactly sure what's wrong here. I've noticed when I remove the #ActiveProfiles annotation that the test fails to run and throws back an exception Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class where it looks like it's trying to get load my actual configuration file and it' cant because I haven't set environment variables that I use in the config file. So How can I get SpringBootTest to pick up on the application-test.yml and proceed with the test?
I have written web service using spring boot.
When I run it it is just doing build and run as JVM
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringTestApplication.class, args);
}
}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.6.RELEASE)
2019-07-10 09:25:01.964 INFO 683 --- [ main] com.example.demo.SpringTestApplication : Starting SpringTestApplication on Amits-MacBook-Air.local with PID 683 (/Users/amitpatil/Downloads/demo/target/classes started by amitpatil in /Users/amitpatil/Downloads/demo)
2019-07-10 09:25:01.967 INFO 683 --- [ main] com.example.demo.SpringTestApplication : No active profile set, falling back to default profiles: default
2019-07-10 09:25:03.298 INFO 683 --- [ main] com.example.demo.SpringTestApplication : Started SpringTestApplication in 7.067 seconds (JVM running for 7.826)
Process finished with exit code 0
Saying that the Process finished with exit code 0 means that everything worked ok. If an exception occurs in your program or otherwise an exit is generated with non-zero argument.
I recommend use Spring Initializr to bootstrap your spring boot application with embedded tomcat. Additionally I suggest Intelliju IDE to work on your application. This should heads up your application development with ease.
This can happen and is totally fine if you have no process defined that is meant to be running forever. For example if you create a web application you will end up with a DispatcherServlet which makes your application stay in running state and waiting for duty.
If that is what you intended to do I would suggest to extend the SpringBootApplication with the proper class. I believe it was ServletInitalizer or something.
I have a very simple Spring Cloud Functions with the AWS adapter to run it as an AWS Lambda. The source code is available on Github: https://github.com/codependent/spring-boot-authentication-function
#SpringBootApplication
class SpringBootAuthenticationFunctionApplication {
#Bean
fun kotlinSupplier(): (Credentials) -> AuthenticationResponse {
return {
if (it.user == "jose" && it.password == "MyPassword") {
//HMAC
val algorithm = Algorithm.HMAC256("secret")
val token = JWT.create()
.withIssuer("MicronautAwsAuthenticationFunction")
.withSubject(it.user)
.sign(algorithm)
AuthenticationResponse(token)
} else {
AuthenticationResponse()
}
}
}
}
fun main(args: Array<String>) {
runApplication<SpringBootAuthenticationFunctionApplication>(*args)
}
When I run this function on AWS Lambda it throws a NullPointerException. Any idea what's wrong?
{
"errorMessage": "java.lang.NullPointerException",
"errorType": "java.lang.NullPointerException",
"stackTrace": [
"org.springframework.cloud.function.context.FunctionalSpringApplication.postProcessApplicationContext(FunctionalSpringApplication.java:102)",
"org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:372)",
"org.springframework.boot.SpringApplication.run(SpringApplication.java:314)",
"org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer.initialize(SpringFunctionInitializer.java:86)",
"org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.initialize(SpringBootStreamHandler.java:55)",
"org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.handleRequest(SpringBootStreamHandler.java:64)",
"sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
"sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)",
"sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)",
"java.lang.reflect.Method.invoke(Method.java:498)"
]
}
I have set org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler as the value of the handler class in the AWS Console.
Full CloudWatch logs:
16:12:39.252 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Searching manifests: [jar:file:/var/runtime/lib/LambdaJavaRTEntry-1.0.jar!/META-INF/MANIFEST.MF, jar:file:/var/runtime/lib/aws-lambda-java-core-1.2.0.jar!/META-INF/MANIFEST.MF, file:/var/task/META-INF/MANIFEST.MF]
16:12:39.258 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Searching manifest: jar:file:/var/runtime/lib/LambdaJavaRTEntry-1.0.jar!/META-INF/MANIFEST.MF
16:12:39.259 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Searching manifest: jar:file:/var/runtime/lib/aws-lambda-java-core-1.2.0.jar!/META-INF/MANIFEST.MF
16:12:39.260 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Searching manifest: file:/var/task/META-INF/MANIFEST.MF
16:12:39.261 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Searching manifests: []
16:12:39.261 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Main class: null
START RequestId: 66460dc7-e90b-487c-a022-12c04ab72c7d Version: $LATEST
16:12:39.272 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Initializing: null
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
2019-03-21 16:12:45.617 ERROR 1 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.NullPointerException: null
at org.springframework.cloud.function.context.FunctionalSpringApplication.postProcessApplicationContext(FunctionalSpringApplication.java:102) ~[task/:na]
at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:372) ~[task/:na]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) ~[task/:na]
at org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer.initialize(SpringFunctionInitializer.java:86) [task/:na]
at org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.initialize(SpringBootStreamHandler.java:55) [task/:na]
at org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.handleRequest(SpringBootStreamHandler.java:64) [task/:na]
at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:888) [LambdaSandboxJava-1.0.jar:na]
at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:293) [LambdaSandboxJava-1.0.jar:na]
at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:64) [LambdaSandboxJava-1.0.jar:na]
at java.lang.Class.forName0(Native Method) [na:1.8.0_181]
at java.lang.Class.forName(Class.java:348) [na:1.8.0_181]
at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:104) [LambdaJavaRTEntry-1.0.jar:na]
java.lang.NullPointerException: java.lang.NullPointerException
java.lang.NullPointerException
at org.springframework.cloud.function.context.FunctionalSpringApplication.postProcessApplicationContext(FunctionalSpringApplication.java:102)
at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:372)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer.initialize(SpringFunctionInitializer.java:86)
at org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.initialize(SpringBootStreamHandler.java:55)
at org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.handleRequest(SpringBootStreamHandler.java:64)
This looks like a bug, so please consider raising the issue here.
I recently started spending more time on Spring Boot and have the started to feel the absence of a shell (similar to Grails console) like application that I can play with the repositories, services etc... Does something like this exist in spring boot?
I recently launched the Spring Context from Groovy project, that gives you access to all beans object within the Remote Shell plugin using the repl groovy interactive console.
Looks like this:
$ ssh -p 2000 user#localhost
user#localhost's password:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.6.RELEASE) on myhost
> repl groovy
Using repl groovy
> ctx.App.myUserService.findUserByEmail("mrsarm#gmail.com")[0].id
100123
> ctx.App.myUserService.getById(100123).name
Mariano Ruiz
> ctx.App.contactDao.logger.effectiveLevel
INFO
> ctx.App.contactDao.logger.setLevel(ch.qos.logback.classic.Level.DEBUG)
As I mentioned in my previous post and later in a comment, you can use the Spring Context from Groovy tool for a Spring Boot 1.x project, but because changes in the Spring Boot framework, it does not work for Spring Boot 2+ projects, in that case you can use the jshell-plugin along with spring-ctx (two projects that I also created).
Quick setup following the steps here and here:
Add the following to your build.gradle:
plugins {
id "com.github.mrsarm.jshell.plugin" version "1.1.0"
}
Add the following dependency to the dependencies section:
implementation 'com.github.mrsarm:spring-ctx:1.0.0'
Add at the end of the repositories section:
maven { url 'https://jitpack.io' }
Any Spring Boot application has a class annotated with #SpringBootApplication that is the starting point of the application, with a public static void main(String[] args) method, you need to create a startup.jsh file at the root of your project calling that method, eg:
com.my.package.MyApplication.main(new String[]{})
You can also add the imports of the business classes you are going to play with, as many as you have, otherwise you can import them once the JShell started:
import com.my.package.services.MyUserService
The configuration is done. You can start the jshell session executing within a console:
$ gradle --console plain jshell
Once the session started you can play with your Spring application, accessing the bean objects from the Spring context as follow:
$ gradle --console plain jshell
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.4.RELEASE)
14:43:28.320 INFO com.my.package.MyApplication : Starting Application on kubu1910 with PID 5225 (/projects/...
14:43:28.323 DEBUG com.my.package.MyApplication : Running with Spring Boot v2.2.4.RELEASE, Spring v5.2.3.RELEASE
14:43:28.324 INFO com.my.package.MyApplication : The following profiles are active: ...
14:43:30.229 INFO boot.web.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8010 (http)
...
...
14:43:33.729 INFO tuate.endpoint.web.EndpointLinksResolver : Exposing 3 endpoint(s) beneath base path ''
14:43:33.811 INFO boot.web.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8010 (http) with context path ''
14:43:33.816 INFO com.my.package.MyApplication : Started Application in 6.995 seconds (JVM running for 10.524)
> Task :jshell
| Welcome to JShell -- Version 11.0.6
| For an introduction type: /help intro
jshell> var myUserService = ctx.App.getBean(MyUserService.class)
jshell> ctx.App.ppjson(myUserService.getByUsername("admin"))
{
"name" : "Jhon",
"lastName" : "Due",
"username" : "admin",
"age" : null
}
Final advice: in Windows / Mac you can call the command with the rlwrap to make the autocompletion to work, there is a well know issue with Gradle that cause tabs and arrow keys to not work well, so you can call the JShell as follow:
$ rlwrap gradle --console plain jshell
Also worth to note that you can use the Gradle Wrapper (./gradlew) from your project if available instead of an installed version of Gradle (gradle).