Spring data redis with kotlin breaks in version 2.3.2.RELEASE - spring-boot

After migrating Spring Boot from 2.3.0 to 2.3.2.RELEASE, my #RedisHash code is broken.
#RedisHash(value = "OtpCache", timeToLive = 5 * 60)
data class OtpCache(#Id val mobile: String,
val otp: String)
interface OtpCacheRepository : CrudRepository<OtpCache, String>
val optional = otpCacheRepository.findById("unknown")
println("optional = " + optional.isPresent)
When I try to find a instance of OtpCache with unknown identifier using findById, instead of getting a Optional.empty(), i see an exception
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method com.example.demo.OtpCache.<init>, parameter mobile
at com.example.demo.OtpCache.<init>(DemoApplication.kt) ~[main/:na]
at com.example.demo.OtpCache_Instantiator_humu2e.newInstance(Unknown Source) ~[main/:na]
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:238) ~[spring-data-commons-2.3.2.RELEASE.jar:2.3.2.RELEASE]
This code was working perfect in Spring Boot 2.3.0.RELEASE
I am using Kotlin data class for creating POJO in my codebase.
Here is link to sample repository
https://github.com/cancerian0684/bug-redishash-springboot

Related

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

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.

Spring 5.2.x to 5.3.x upgrade cause ClassNotFoundException for Quartz Jobs under Wildlfy 20

We are using:
JDK 8
Spring 5.2.16.RELEASE
Quartz - 2.3.2
Wildfly 20
Apart from the web application(.war) we have also plugins (.jar) for which the classes are loaded with the following code in the web app:
Thread.currentThread().setContextClassLoader(pluginsClassLoader);
PluginsClassLoader extends URLClassLoader
We are using
org.springframework.scheduling.quartz.QuartzBean to define our own quartz jobs both in the war in the plugins (.jar) e.g. FSSendMessagesWorker extends QuartzBean
in a #Configuration class we have
#Bean
public JobDetailFactoryBean fsPluginSendMessagesWorkerJob() {
JobDetailFactoryBean obj = new JobDetailFactoryBean();
obj.setJobClass(FSSendMessagesWorker.class);
obj.setDurability(true);
return obj;
}
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public SimpleTriggerFactoryBean fsPluginSendMessagesWorkerTrigger()
SimpleTriggerFactoryBean obj = new SimpleTriggerFactoryBean();
obj.setJobDetail(fsPluginSendMessagesWorkerJob().getObject());
obj.setRepeatInterval("* * * 0 1");
obj.setStartDelay(20000);
return obj;
}
After upgrading to Spring 5.3.x it looks like the all classes which extends QuartzBean and present in the .jar files could not be loaded while this was possible (worked) before in Spring 5.2.x
The ones present in the main app (.war) just works fain - are loaded.
Apart from Spring no other library was upgraded.
021-10-05 14:03:25,055 [] [] [] [EE-ManagedExecutorService-quartzExecutorService-Thread-1] ERROR o.s.s.q.LocalDataSourceJobStore:2867 - Error retrieving job, setting trigger state to ERROR.
org.quartz.JobPersistenceException: Couldn't retrieve job because a required class was not found: com.company.plugin.fs.worker.FSSendMessagesWorker from [Module "deployment.mycompany.war" from Service Module Loader]
at deployment.mycompany.war//org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveJob(JobStoreSupport.java:1393)
at deployment.mycompany.war//org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTrigger(JobStoreSupport.java:2864)
at deployment.mycompany.war//org.quartz.impl.jdbcjobstore.JobStoreSupport$41.execute(JobStoreSupport.java:2805)
at deployment.mycompany.war//org.quartz.impl.jdbcjobstore.JobStoreSupport$41.execute(JobStoreSupport.java:2803)
at deployment.mycompany.war//org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3864)
at deployment.mycompany.war//org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTriggers(JobStoreSupport.java:2802)
at deployment.mycompany.war//org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:287)
at org.jboss.as.ee#20.0.1.Final//org.jboss.as.ee.concurrent.ControlPointUtils$ControlledRunnable.run(ControlPointUtils.java:105)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.glassfish.javax.enterprise.concurrent//org.glassfish.enterprise.concurrent.internal.ManagedFutureTask.run(ManagedFutureTask.java:117)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
at org.glassfish.javax.enterprise.concurrent//org.glassfish.enterprise.concurrent.ManagedThreadFactoryImpl$ManagedThread.run(ManagedThreadFactoryImpl.java:227)
Caused by: java.lang.ClassNotFoundException: com.company.plugin.fs.worker.FSSendMessagesWorker from [Module "deployment.mycompany.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 deployment.domibus-MSH-wildfly-5.0-SNAPSHOT.war//org.springframework.util.ClassUtils.forName(ClassUtils.java:284)
at deployment.domibus-MSH-wildfly-5.0-SNAPSHOT.war//org.springframework.scheduling.quartz.ResourceLoaderClassLoadHelper.loadClass(ResourceLoaderClassLoadHelper.java:81)
at deployment.domibus-MSH-wildfly-5.0-SNAPSHOT.war//org.springframework.scheduling.quartz.ResourceLoaderClassLoadHelper.loadClass(ResourceLoaderClassLoadHelper.java:87)
at deployment.domibus-MSH-wildfly-5.0-SNAPSHOT.war//org.quartz.impl.jdbcjobstore.StdJDBCDelegate.selectJobDetail(StdJDBCDelegate.java:852)
at deployment.domibus-MSH-wildfly-5.0-SNAPSHOT.war//org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveJob(JobStoreSupport.java:1390)
The quartz has the option to register class loader via the property:
org.quartz.scheduler.classLoadHelper.class
In case setting the TCCL Transaction Context ClassLoader, make sure the cron thread has the same class loader as the class loader, which loads the "classes." In WildFly new threads, get ModuleClassLoader which can have different class scope than the one set back to thread scope in the initial stage.
Try with:
org.quartz.scheduler.classLoadHelper.class = org.quartz.simpl.InitThreadContextClassLoadHelper
Or write a custom ClassHelperLoader class.
https://www.quartz-scheduler.org/api/2.0.2/org/quartz/spi/ClassLoadHelper.html

Spring Boot & RabbitMQ: message conversion content type

We are using Spring Boot 2.1.3.RELEASE and RabbitMQ. MessagePack is used for serialization.
We are using convertAndSend:
rabbitTemplate.convertAndSend("exchange", "routingKey", object);
And getting the following exception:
java.lang.UnsupportedOperationException: null
at org.msgpack.jackson.dataformat.MessagePackFactory.createGenerator(MessagePackFactory.java:102)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3219)
at org.springframework.amqp.support.converter.AbstractJackson2MessageConverter.createMessage(AbstractJackson2MessageConverter.java:259)
at org.springframework.amqp.support.converter.AbstractMessageConverter.toMessage(AbstractMessageConverter.java:70)
at org.springframework.amqp.support.converter.AbstractMessageConverter.toMessage(AbstractMessageConverter.java:58)
at org.springframework.amqp.rabbit.core.RabbitTemplate.convertMessageIfNecessary(RabbitTemplate.java:1726)
at org.springframework.amqp.rabbit.core.RabbitTemplate.convertAndSend(RabbitTemplate.java:1048)
at org.springframework.amqp.rabbit.core.RabbitTemplate.convertAndSend(RabbitTemplate.java:1041)
This is the line where the exception is thrown:
return getRequiredMessageConverter().toMessage(object, new MessageProperties());
Is there a way to set the content type in MessageProperties to application/json? The default is application/octet-stream.

Spring Boot with Embedded Mongo : Cannot assign requested address: JVM_Bind

I am trying to setup a JUnit test for a Spring Boot with embedded Mongo & Kafka :-
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = {AccountingApplication.class})
#DataMongoTest
public class BaseEmbeddedTest {
#ClassRule
public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true);
#Autowired
private MongoTemplate mongoTemplate;
#Test
public void emptyTest(){
}
}
src/test/resources/application.yml :-
spring:
data:
mongodb:
port: 0
kafka:
bootstrap-servers: ${spring.embedded.kafka.brokers}
PROBLEM
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [de.flapdoodle.embed.mongo.config.IMongodConfig]: Factory method 'embeddedMongoConfiguration' threw exception; nested exception is java.net.BindException: Cannot assign requested address: JVM_Bind
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
... 140 more
Caused by: java.net.BindException: Cannot assign requested address: JVM_Bind
at java.net.DualStackPlainSocketImpl.bind0(Native Method)
at java.net.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:106)
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:190)
at java.net.ServerSocket.bind(ServerSocket.java:375)
at java.net.ServerSocket.<init>(ServerSocket.java:237)
at de.flapdoodle.embed.process.runtime.Network.getFreeServerPort(Network.java:80)
at org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration.embeddedMongoConfiguration(EmbeddedMongoAutoConfiguration.java:147)
What am I doing wrong here ?
Version:-
dependencyManagementPluginVersion = '1.0.3.RELEASE'
springBootVersion = '1.5.6.RELEASE'
springCloudVersion = 'Dalston.SR2'
projectVersion = '0.0.1-SNAPSHOT'
javaVersion = 1.8
kotlinVersion = '1.1.4'
This annotation: #DataMongoTest causes Spring Boot to create an embedded Mongo instance. The exception messages tells us that the embedded Mongo instance cannot start because there is already a process running on the port it is trying to run on.
The embedded Mongo instance is configured by EmbeddedMongoAutoConfiguration and the strategy applied by Spring Boot - for port allocation - is as follows:
if configured Mongo port > 0 then
use the configured port
else
assign a random port
end
So, I suspect that your test context is configured with a non zero value for spring.data.mongodb.port. I know you posted your application.yml which implies that you are - correctly - assigning a zero value to spring.data.mongodb.port but if you put a breakpoint inside the EmbeddedMongoAutoConfiguration constructor and peek inside the properties parameter I think you'll see that the actual value in use by that configuration class is not zero. If the port value passed to EmbeddedMongoAutoConfiguration is actually zero but you are still getting the JVM_Bind error then that implies that this call: Network.getFreeServerPort(this.getHost()) is not returning a free port and that seems unlikely.
In order to fix this issue: as long as you configure your test context with spring.data.mongodb.port=0 then the embedded Mongo instance will be assigned a random port and this random port will be made known to other aspects of your Spring context (such as your MongoTemplate) which need to talk to that Mongo instance.

Wiring Activiti as a Spring Bean in Grails

I'm trying to create a Grails application that uses Activiti for its process engine. To that end, I'd like the main Activiti service classes (RuntimeService, TaskService, etc.) to be wired as Spring beans.
I believe that I have the wiring setup correctly, but when I run a simple integration test that calls the runtime service, I get an error that Spring couldn't open a hibernate session (see Full Stack Trace, below).
Update: I can start the application with run-app, then call a controller action which calls my service, and it all works. So, the Activiti wiring works, it just has some conflict with the Grails Integration Testing mixin.
I really want the Activiti services to use the same datasource connection as the Grails application. I'm assuming that the problem is that Activiti is trying to create its own ConnectionHolder instance, when Grails already has one setup for the integration tests.
My specific (and possibly misguided) question I have is, how do I configure my Activiti ProcessEngine so that it uses the same data source and hibernate connection that my Grails application does?
The more general question is, how can I best make the Activiti services available to my Grails application? I've looked at the Activiti plugin for grails, and the source of it has helped me get this far. However, I'd rather not use that plugin; it's not using the latest activiti, development on it is not terribly active, and it's not really what I need in any case.
Full Stack Trace
| Failure: start approver request(com.package.MyServiceSpec)
| org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#7f2e1821] for key [org.springframework.jdbc.datasource.LazyConnectionDa
at grails.test.mixin.integration.IntegrationTestMixin.initIntegrationTest(IntegrationTestMixin.groovy:58)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.runtime.extension.builtin.JUnitFixtureMethodsExtension$FixtureType$FixtureMethodInterceptor.intercept(JUnitFixtureMethodsExtension.java:145)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:84)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
Caused by: org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#7f2e1821] for key [org.springframework.jdbc.datasource.LazyConn
... 7 more
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#7f2e1821] for key [org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy#1b7aeb] bound to thread [main]
... 7 more
| Completed 1 integration test, 1 failed in 0m 1s
resources.groovy
import org.activiti.engine.ProcessEngine
import org.activiti.spring.ProcessEngineFactoryBean
import org.activiti.explorer.form.*
import org.activiti.spring.SpringProcessEngineConfiguration
import grails.util.Environment
//These imports are only needed in the test environment for building an h2 database for activiti during unit tests
import org.springframework.jdbc.datasource.DataSourceTransactionManager
import org.springframework.jdbc.datasource.SimpleDriverDataSource
beans = {
processEngineConfig(SpringProcessEngineConfiguration) {
dataSource = ref('dataSource')
transactionManager = ref('transactionManager')
databaseType = 'oracle'
databaseSchema = 'OURSCHEMA'
databaseSchemaUpdate = false
jobExecutorActivate = true
}
processEngine(ProcessEngineFactoryBean) {
processEngineConfiguration = ref("processEngineConfig")
}
runtimeService(processEngine: "getRuntimeService")
repositoryService(processEngine: "getRepositoryService")
taskService(processEngine: "getTaskService")
managementService(processEngine: "getManagementService")
historyService(processEngine: "getHistoryService")
formService(processEngine: "getFormService")
}
Service class
class MyService {
def foapAuthFoapService
def processEngine
def runtimeService
def repositoryService
def taskService
def managementService
def historyService
def formService
/**
* Start the activiti process.
*
*/
def startRequest(String requester, String subject, String designatedApprover) {
runtimeService.startProcessInstanceByKey('MyProcess', ["requester": requester, "subject": subject, "designatedApprover": designatedApprover])
}
}
Spock Test
def "start request"() {
setup:
def approverRequest = service.startRequest(requester, subject, designatedApprover)
def variables = runtimeService.getVariables(approverRequest.id) //approverRequest.getProcessVariables()
expect:
approverRequest instanceof ProcessInstance
variables.entrySet().contailsAll(["designatedApprover": designatedApprover, "requester": requester, "subject": subject].entrySet())
where:
requester | subject | designatedApprover
"abc123" | "def456"| "hij789"
}
we at Alephsa are maintaining the grails activiti plugin updated to the latest version of Activiti (5.17) and we are working to update to version 6. You can find the plugin at bintray also with the grails activiti spring security plugin. In our github account you can find the source code to guide you in your efforts.
Regards.

Resources