Reputation: 31
I am trying to migrate from Spring Boot 2.7 application with Hazelcast 4.2 to Spring Boot 3.2 and Hazelcast 5.3, which also includes JCache Hazelcast provider.
In previous version I was using WebFilter from Hazelcast (https://github.com/hazelcast/hazelcast-wm/blob/master/src/main/java/com/hazelcast/web/WebFilter.java) registered with FilterRegistrationBean, but it is not prepared for Spring Boot 3 bacause of the lack of jakarta imports.
Because of that I wanted to use Spring Session Hazelcast 3 configuration, but after making request to application I am getting this stacktrace:
com.hazelcast.nio.serialization.HazelcastSerializationException: java.lang.ClassNotFoundException: org.springframework.session.hazelcast.SessionUpdateEntryProcessor
at com.hazelcast.internal.serialization.impl.defaultserializers.JavaDefaultSerializers$JavaSerializer.read(JavaDefaultSerializers.java:96)
at com.hazelcast.internal.serialization.impl.defaultserializers.JavaDefaultSerializers$JavaSerializer.read(JavaDefaultSerializers.java:85)
at (...)
Caused by: java.lang.ClassNotFoundException: org.springframework.session.hazelcast.SessionUpdateEntryProcessor
at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
(...)
So then I added configuration for the hazelcast server and client to use "User Code Deployment" as stated in the documentation (along with Spring one): https://docs.hazelcast.com/hazelcast/5.3/clusters/deploying-code-on-member
but then I get error on missing class with @SessionScope
- do I have to add jar of classes?
com.hazelcast.core.HazelcastException: java.lang.NoClassDefFoundError: com/g4iner/ExampleCache$Companion
at com.hazelcast.internal.util.ExceptionUtil.lambda$static$0(ExceptionUtil.java:48)
I get this error after following this documentantion, but I'm not sure if I adjusted this in a right way: https://docs.spring.io/spring-session/reference/3.2/http-session.html#httpsession-hazelcast
There is also some information on Hazelcast but it lacks changes for new versions: https://docs.hazelcast.com/tutorials/spring-session-hazelcast
My configuration and code (using Spring Boot 3.2.6, Hazelcast 5.3.7 client, hazelcast 5.3.7 server running in docker-compose):
docker-compose.yml
version: "3"
services:
hazelcast:
image: hazelcast/hazelcast:5.3.7-slim-jdk17
environment:
- HZ_USERCODEDEPLOYMENT_ENABLED=true
ports:
- "5701:5701"
pom.xml
org.springframework.boot:spring-boot-starter-web-3.2.6
org.springframework.boot:spring-boot-starter-cache-3.2.6
org.springframework.session:spring-session-core-3.2.3
org.springframework.session:spring-session-hazelcast-3.2.3
javax.cache:cache-api-1.1.1
com.hazelcast:hazelcast-spring-5.3.7
com.hazelcast:hazelcast-5.3.7
application.yml
spring:
cache:
type: jcache
jcache:
provider: com.hazelcast.client.cache.HazelcastClientCachingProvider
hazelcast:
config: classpath:hazelcast-client.xml
hazelcast-client.xml
<hazelcast-client xmlns="http://www.hazelcast.com/schema/client-config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/client-config
http://www.hazelcast.com/schema/client-config/hazelcast-client-config-5.3.xsd">
<cluster-name>dev</cluster-name>
<instance-name>hazelcast-client</instance-name>
<user-code-deployment enabled="true">
<classNames>
<className>org.springframework.session.hazelcast.SessionUpdateEntryProcessor</className>
<className>org.springframework.session.Session</className>
<className>org.springframework.session.MapSession</className>
</classNames>
</user-code-deployment>
<network>
<cluster-members>
<address>localhost:5701</address>
</cluster-members>
</network>
</hazelcast-client>
HazelcastSessionConfiguration.kt
@EnableHazelcastHttpSession
@Configuration
open class HazelcastSessionConfiguration {
@EnableHazelcastHttpSession
@Configuration
open class HazelcastSessionConfiguration {
@Bean
@SpringSessionHazelcastInstance
open fun hazelcastInstance(): HazelcastInstance {
val clientConfig = ClientConfig()
clientConfig.networkConfig.addAddress("localhost:5701")
clientConfig.userCodeDeploymentConfig.setEnabled(true).addClass(Session::class.java)
.addClass(MapSession::class.java).addClass(SessionUpdateEntryProcessor::class.java)
return HazelcastClient.newHazelcastClient(clientConfig)
}
// example config from Spring docs for Server - what and how do I need for docker instance?
@Bean
open fun hazelcastConfig(): Config {
val config = Config()
val attributeConfig = AttributeConfig()
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractorClassName(PrincipalNameExtractor::class.java.name)
config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addAttributeConfig(attributeConfig)
.addIndexConfig(
IndexConfig(IndexType.HASH, HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
)
config.setClusterName("dev")
config.networkConfig.setPublicAddress("localhost:5701")
val serializerConfig = SerializerConfig()
serializerConfig.setImplementation(HazelcastSessionSerializer()).setTypeClass(MapSession::class.java)
config.serializationConfig.addSerializerConfig(serializerConfig)
return config
}
}
I tried different configurations,with and without "hazelcastConfig" bean, changing hazelcast to 5.4 and 4.2, or creating "hazelcastInstance" but nothing changes.
What am I missing here, what is the proper way of configuring client and server? Any help will be appreciated
Upvotes: 0
Views: 238
Reputation: 702
If you are using Hazelcast as a client, then the "user code deployment" feature needs to be enabled. Because spring session is using Hazelcast's entry processor to update the session data.
On the cluster side you need to change the docker-compose.yaml file something like this. Here I am using hazelcast:5.4.0 image because Spring 3 requires Java 17. As far as I know Hazelcast 5.3.x uses Java 8 and it does not allow a class compiled with a newer Java version to be deployed.
version: "3"
services:
hazelcast:
image: hazelcast/hazelcast:5.4.0-slim-jdk21
ports:
- "5701:5701"
environment:
- HZ_USERCODEDEPLOYMENT_ENABLED=true
On the client side you need to deploy some spring classes to cluster, because Hazelcast cluster does not have them. Something like this.
import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.SessionIdGenerator;
import org.springframework.session.hazelcast.SessionUpdateEntryProcessor;
@Configuration
public class HazelcastHttpSessionConfig {
@Bean
public HazelcastInstance hazelcastInstance() {
ClientConfig clientConfig = new ClientConfig();
clientConfig.getNetworkConfig()
.addAddress("0.0.0.0:5701"); // connect to local server
clientConfig.getUserCodeDeploymentConfig()
.setEnabled(true)
.addClass(Session.class)
.addClass(MapSession.class)
.addClass(SessionUpdateEntryProcessor.class)
.addClass(SessionIdGenerator.class)
;
return HazelcastClient.newHazelcastClient(clientConfig);
}
}
application.properties is like this
spring.cache.type=jcache
spring.cache.jcache.provider: com.hazelcast.client.cache.HazelcastClientCachingProvider
That's all. You don't need to do anything else.
Note : This answer only works if you are using spring-session only. If you are using spring-session + spring-security you also need to deploy spring-security related classes to Hazelcast cluster
Upvotes: 1