Startup error in Spring Cloud Stream after upgrading to Spring Boot 2.6.1 and Spring Cloud 2021.0.0 - spring

I just upgraded a Spring Boot application that uses Spring Cloud Stream Kafka producers and consumers to
plugins {
id("org.springframework.boot") version "2.6.1"
...
}
extra["springCloudVersion"] = "2021.0.0"
extra["springCloudStreamVersion"] = "3.2.1"
The applications doesn't start anymore with the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'functionInitializer' defined in class path resource [org/springframework/cloud/stream/function/FunctionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ClassCastException: class reactor.core.publisher.MonoPeekTerminal cannot be cast to class reactor.core.publisher.Flux (reactor.core.publisher.MonoPeekTerminal and reactor.core.publisher.Flux are in unnamed module of loader 'app')
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
...
Caused by: java.lang.ClassCastException: class reactor.core.publisher.MonoPeekTerminal cannot be cast to class reactor.core.publisher.Flux (reactor.core.publisher.MonoPeekTerminal and reactor.core.publisher.Flux are in unnamed module of loader 'app')
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.reactorFluxStream(TraceFunctionAroundWrapper.java:187)
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.reactorStream(TraceFunctionAroundWrapper.java:120)
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.doApply(TraceFunctionAroundWrapper.java:97)
at org.springframework.cloud.function.context.catalog.FunctionAroundWrapper.apply(FunctionAroundWrapper.java:47)
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$1.doApply(SimpleFunctionRegistry.java:256)
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.apply(SimpleFunctionRegistry.java:550)
at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder.bindFunctionToDestinations(FunctionConfiguration.java:512)
at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder.afterPropertiesSet(FunctionConfiguration.java:418)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
... 16 common frames omitted
Did I miss any upgrade guide or is it a bug?
Producer
#Component
class EventProducer(#Qualifier("eventSink") private val eventProcessor: Sinks.Many<Message<EventReceived>>) {
private val logger = LoggerFactory.getLogger(javaClass)
fun send(event: EventReceived): Mono<EventReceived> {
return Mono.defer {
val message = MessageBuilder.withPayload(event)
.setHeader(MESSAGE_KEY, event.id)
.setHeader(TIMESTAMP, OffsetDateTime.now().toInstant().toEpochMilli())
.build()
logger.info("Sending event {}", event)
while (eventProcessor.tryEmitNext(message).isFailure) {
LockSupport.parkNanos(10)
}
event.toMono()
}.subscribeOn(Schedulers.boundedElastic())
}
Consumer
#Configuration
class MetricConsumer(...) {
private val logger = LoggerFactory.getLogger(javaClass)
#Bean
fun consumeMetricUpdated(): Function<Flux<Message<MetricUpdated>>, Mono<Void>> {
...
}

This looks like a bug in s-c-sleuth. I'll follow up with Marcin about this.
Can you please post the signature of your function as well, need to confirm something?
Meanwhile, you can temporarily disconnect sleuth's TraceFunctionAroundWrapper by setting spring.sleuth.function.enabled to false.

Related

Wildfly / Infinispan HTTP session replication hits ClassNotFoundException when unmarshalling CGLIB Session Bean

I'm running Wildfly 20.0.1.Final in standalone, two-node cluster. I'm trying to implement HTTP Session sharing between the nodes.
In my Spring web application I have <distributable/> in my web.xml.
My session object is this:
package my.package;
#Component
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
public class MySessionBean implements Serializable {
// omitted for brevity
}
As you can see, I have ScopedProxyMode.TARGET_CLASS.
When I perform a failover in Wildfly, my HTTP Session can't be restored however, as I hit this warning:
2021-02-22 13:24:18,651 WARN [org.wildfly.clustering.web.infinispan] (default task-1) WFLYCLWEBINF0007:
Failed to activate attributes of session Pd9oI0OBiZSC9we0uXsZdBwkLnadO1l4TUfvoJZf:
org.wildfly.clustering.marshalling.spi.InvalidSerializedFormException:
java.lang.ClassNotFoundException: my.package.MySessionBean$$EnhancerBySpringCGLIB$$9c0fa1df
from [Module "deployment.myDeployment.war" from Service Module Loader]
...
Caused by: java.lang.ClassNotFoundException: my.package.MySessionBean$$EnhancerBySpringCGLIB$$9c0fa1df from [Module "deployment.myDeployment.war" from Service Module Loader]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:255)
at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:398)
at org.jboss.marshalling#2.0.9.Final//org.jboss.marshalling.ModularClassResolver.resolveClass(ModularClassResolver.java:133)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadClassDescriptor(RiverUnmarshaller.java:1033)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadNewObject(RiverUnmarshaller.java:1366)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:283)
at org.jboss.marshalling.river#2.0.9.Final//org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:216)
at org.jboss.marshalling#2.0.9.Final//org.jboss.marshalling.AbstractObjectInput.readObject(AbstractObjectInput.java:41)
at org.wildfly.clustering.marshalling.spi#20.0.1.Final//org.wildfly.clustering.marshalling.spi.util.MapExternalizer.readObject(MapExternalizer.java:65)
...
Note, that the ClassNotFoundException is complaining because the lack of my.package.MySessionBean$$EnhancerBySpringCGLIB$$9c0fa1df, which is the Spring-enhanced bean of my MySessionBean bean.
Changing to ScopedProxyMode.INTERFACES is not an option.
Can you please point me in the right direction with this?
I managed to fix this by creating a simple POJO, called MySessionDTO, and using that in my session.
So initially I had this (which threw the exception in the question):
request.getSession().setAttribute("mySession", mySessionBean);
...and after I created MySessionDTO (see below), I refactored it into this:
request.getSession().setAttribute("mySession", mySessionBean.getMySessionDTO());
MySessionDTO is a simple POJO:
package my.package;
import java.io.Serializable;
public class MySessionDTO extends MySessionBean implements Serializable {
public MySessionDTO (MySessionBean mySessionBean) {
this.setAttributeX(mySessionBean.getAttributeX());
this.setAttributeY(mySessionBean.getAttributeY());
}
}

ConfigurationProperties Not Working in Spring Boot2

Spring Boot Version 2.1.4 RELEASE
I have client jar with a Settings Bean defined as
#ConditionalOnBean(annotation = Some.class)
#ConfigurationProperties(prefix = "service")
#Data
public class WebserviceSettings {
//bunch of Properties
private String serviceUrl;
#PostConstruct
public void init() { if(serviceUrl==null) throw ValidationException("bla") }
}
Another Config Class
#ConditionalOnBean(annotation = Some.class)
#EnableConfigurationProperties(WebserviceSettings.class)
#Configuration
public class ClientConfig{
#Bean("webserviceSettings")
public WebserviceSettings webserviceSettings(){
return new WebserviceSettings();
}
}
Client jar is added to spring boot rest service.
The JUNIT Test written for the Client jar works fine and loads up the Bean without any validation Exception coming out of the PostConstruct Method.
Test Class written within the client works!
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { ClientTestBootApplication.class })
#TestPropertySource(properties="spring.config.location=classpath:application-test.yaml")
However, when I am loading the Spring Boot Service - it fails to load the Settings bean with ValidationException thrown from PostConstruct Method.
application.yml
service:
url: "bla"
It means that when the client is added to services project, it's not loading the property from yaml. How to solve this?
Project Structure
spring-boot-restEasy Application
- src/main/resources/application-DEV.yml
--client jar A
--client jar B
Client jar = [Service Call and some Business Logic],
application-DEV.yml Contains Service Settings of Client Jar like url
Exception
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service-packagName.Settings': Invocation of init method failed; nested exception is ValidationException: [url cannot be Null]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:139)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:414)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1754)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1247)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
... 52 more
Since you already mentioned the service as a prefix in the following annotation on the class WebserviceSettings.java.
#ConfigurationProperties(prefix = "service"),
then no need to add service as a prefix in the variable (private String serviceUrl;).
To correct it: Change private String serviceUrl; to private String url;
in your application.yml, you have :
service:
url: greetings
Your config class must look like :
#ConditionalOnBean(annotation = Some.class)
#ConfigurationProperties(prefix = "service")
#Data
public class WebserviceSettings {
//bunch of Properties
private String url; // do not need to use service because you've already specified it in the prefix
#PostConstruct
public void init() { if(serviceUrl==null) throw ValidationException("bla") }
}

SpringBootTest failing with 'IllegalStateException - No ServletContext set'

I have a pretty simple #SpringBootTest :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {...})
public class MyApplicationTest {
#Test
public void anyTest() { ... }
}
It was working as expected until we've added #EnableSchedulerLock (from Shedlock) on the MyApplication.java file.
Since, we're experiencing this issue :
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:582)
... 46 common frames omitted
Caused by: java.lang.IllegalStateException: No ServletContext set
at org.springframework.util.Assert.state(Assert.java:73)
at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:483)
This happends when Spring tries to instanciate its resourceHandlerMapping :
#Bean
public HandlerMapping resourceHandlerMapping() {
Assert.state(this.applicationContext != null, "No ApplicationContext set");
Assert.state(this.servletContext != null, "No ServletContext set");
...
It's like this #Bean is created before the setServletContext (from WebMvcConfigurationSupport) is called.
As mentioned by Lukas, this error has been fixed in the latest version of Shedlock.
Upgrading Shedlock's version from 2.2.0 to 2.5.0 fixes the issue.

How to ignore the spring-boot-cassandra default config to load the cassandra connection instance

I have add the dependency of cassandra starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
but the default config is poor for me.
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'cassandraSession'
defined in class path resource
[org/springframework/boot/autoconfigure/data/cassandra/
CassandraDataAutoConfiguration.class]:
Invocation of init method failed; nested exception is
com.datastax.driver.core.exceptions.NoHostAvailableException:
All host(s) tried for query failed (tried: localhost/0:0:0:0:0:0:0:1:9042
(com.datastax.driver.core.exceptions.TransportException:
[localhost/0:0:0:0:0:0:0:1:9042] Cannot connect), localhost/127.0.0.1:9042
(com.datastax.driver.core.exceptions.TransportException:
[localhost/127.0.0.1:9042] Cannot connect))
Caused by: com.datastax.driver.core.exceptions.NoHostAvailableException:
All host(s) tried for query failed (tried: localhost/0:0:0:0:0:0:0:1:9042
(com.datastax.driver.core.exceptions.TransportException:
[localhost/0:0:0:0:0:0:0:1:9042]
Cannot connect), localhost/127.0.0.1:9042
(com.datastax.driver.core.exceptions.TransportException:
[localhost/127.0.0.1:9042] Cannot connect))
I hope the spring application not load the cassandra connection instance(like cassandraSession) when I don't haved config the 'spring.data.cassandra.*'
What can do it?
You need to exclude CassandraDataAutoConfiguration to disable spring boot cassandra auto configuration e.g.
#SpringBootApplication
#EnableAutoConfiguration(exclude = { CassandraDataAutoConfiguration.class })
public class Application {
}
Then define your own Cassandra configuration e.g.
#Configuration
#EnableReactiveCassandraRepositories
public class CassandraConfig extends AbstractReactiveCassandraConfiguration {
}
Ended up defining my own custom cluster bean
#Configuration
#EnableReactiveCassandraRepositories
public class CassandraConfig extends AbstractReactiveCassandraConfiguration {
// read contact points from config
#Value("${spring.data.cassandra.contact-points}")
private String contactPoints;
#Override
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean bean = super.cluster();
bean.setContactPoints(contactPoints);
return bean;
}
https://github.com/spring-projects/spring-data-cassandra/blob/f3115017d4a04e105d4046f6fd716ac308ecd7aa/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/AbstractClusterConfiguration.java#L88

Usage of Spring #ConfigurationProperties gives nullpointer when running tests, JHipster app

I have implemented a simple file upload routine using Spring Boot ConfigurationProperties annotation so I can load the directory from my YAML configuration file:
Service:
#Service
public class FileSystemStorageService implements StorageService {
private final Logger log = LoggerFactory.getLogger(FileSystemStorageService.class);
private final Path pictureLocation;
#Autowired
public FileSystemStorageService(StorageProperties storageProperties) {
pictureLocation = Paths.get(storageProperties.getUpload());
}
And the StorageProperties:
#Component
#ConfigurationProperties("nutrilife.meals")
public class StorageProperties {
private String upload;
public String getUpload() {
return upload;
}
public void setUpload(String upload) {
this.upload= upload;
}
}
In the yaml file I have:
nutrilife:
meals:
upload: /$full_path_to_my_upload_dir
This works perfectly in normal Spring Boot runtime but the problem starts when I try to run my integration tests, it throws the error:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fileSystemStorageService' defined in file [/home/mmaia/git/nutrilife/build/classes/main/com/getnutrilife/service/upload/FileSystemStorageService.class]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.getnutrilife.service.upload.FileSystemStorageService]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:279)
...
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.getnutrilife.service.upload.FileSystemStorageService]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154)
So basically during tests the YAML file configuration looks like it's not being properly picked up. I am new to Spring. How can I fix it?
try adding the #value annotation :
#Value("upload")
private String upload;
The above answer is valid for application.properties config.
it also works with yaml as well.
You may find your correct correct yaml config here :
24.6 Using YAML instead of Properties

Resources