Currently, I'm using spring cache by using #Cachable with Ehcache. I'm about to replace Ehcache with Redis by using Spring Data Redis 2.0.3. All examples I see on the web are based on the old versions of it but the new version has the different format of constructors.
This is my current cacheManager conf:
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
<property name="shared" value="true"/>
</bean>
The example of using Redis based on the older version is:
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
c:template-ref="redisTemplate" />
The constructor in the new version is totally different from the older versions and all examples of the new version put everything into cache manually like this:
redisTemplate.opsForHash().put(user.getObjectKey(), user.getKey(), user);
I still want to use cacheManager and #cachable but have no idea how to configure the cacheManager bean with the new version of Spring Data Redis. The constructor of the new versions needs RedisCacheWriter:
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration)
I appreciate if you can share your idea about how to set up cacheManager to use #Cachable with the new version of Spring Data Redis(min 2.0.3).
In Spring Data Redis 2.0+,you could write like this:
/**
* Redis config.
*
* #author chenxinyu
*/
#Configuration
#EnableCaching
public class RedisConfig {
#Value("${spring.redis.host}")
private String redisHost;
#Value("${spring.redis.port}")
private Integer redisPort;
#Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration configuration =
new RedisStandaloneConfiguration(redisHost, redisPort);
return new JedisConnectionFactory(configuration);
}
#Bean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
RedisSerializer stringRedisSerializer = new StringRedisSerializer();
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
#Bean
public CacheManager initRedisCacheManager(RedisConnectionFactory factory) {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.RedisCacheManagerBuilder.fromConnectionFactory(factory);
return builder.build();
}
}
this is just one example, you can see more methods in RedisCacheManager.java.
Related
I am using Spring boot (version 2.2.7.RELEASE hence spring rabbit 2.2.6) and RabbitMQ 3.3.5. why RabbitTemplate can not use direct-reply? is there any configuration with the broker?
spring rabbit config:
#Configuration
#EnableRabbit
public class RabbitConfig {
private String host = "localhost";
private int port = 5672;
private String username = "admin";
private String password = "admin";
private String exchangeName = "xxx";
private String queueName = "qqq";
#Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost(this.host);
factory.setPort(this.port);
factory.setUsername(this.username);
factory.setPassword(this.password);
return factory;
}
#Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory factory){
RabbitAdmin admin = new RabbitAdmin(factory);
return admin;
}
#Bean
public Queue queue(){
return new Queue(this.queueName, true);
}
#Bean
public Exchange exchange(){
return new DirectExchange(this.exchangeName, true, false);
}
#Bean
public Binding binding(Queue queue, Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(queue.getName()).noargs();
}
#Bean
public RabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory cFactory){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(cFactory);
factory.setMessageConverter(messageConverter());
factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
return factory;
}
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory factory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(factory);
rabbitTemplate.setMessageConverter(messageConverter());
return rabbitTemplate;
}
#Bean
public MessageConverter messageConverter(){
//return new Converter();
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
jackson2JsonMessageConverter.getJavaTypeMapper().addTrustedPackages("com.mhr.xp.amqp", "java.lang");
return jackson2JsonMessageConverter;
}
}
here I use RabbitTemplate:
Object o = rabbitTemplate.convertSendAndReceive("xxx", "qqq", new Dto("ggg"));
I get the following warnning:
2020-05-15 23:06:59.188 WARN 15681 --- [ main] o.s.amqp.rabbit.core.RabbitTemplate : Broker does not support fast replies via 'amq.rabbitmq.reply-to', temporary queues will be used: channel error; protocol method: #method(reply-code=404, reply-text=NOT_FOUND - no queue 'amq.rabbitmq.reply-to' in vhost '/', class-id=50, method-id=10).
Direct reply-to was added to RabbitMQ in version 3.4.
The current version is 3.8.3.
https://www.rabbitmq.com/download.html
https://docs.spring.io/spring-amqp/docs/2.2.6.RELEASE/reference/html/#direct-reply-to
Starting with version 3.4.0, the RabbitMQ server supports direct reply-to. This eliminates the main reason for a fixed reply queue (to avoid the need to create a temporary queue for each request). Starting with Spring AMQP version 1.4.1 direct reply-to is used by default (if supported by the server) ...
I have created a small API based on NancyFx on .NET Core 2. It uses AutoFac as the IOC container and NHibernate 5.3 to access the database.
I have run into a problem with threading and the CurrentSessionContext. Basically when I enter the AfterRequest pipeline, I am usually on another thread, and then the CurrentSessionContext doesn't know about the binding I did at the beginning of the request.
I have tried to use the WebSessionContext instead, but since I am using the stack I am, there is no HttpContext.Current. To get access to the HttpContext you have to inject the Microsoft.AspNetCore.Http.IHttpContextAccessor where you need it.
How can I tell NHibernate to bind to my own context somehow, so I my session isn't lost between BeforeRequest and AfterRequest?
To make it easy to wrap my data accecss in a transaction I have added the following to my Nancy Bootstrapper:
protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
ConfigureNHibernateSessionPerRequest(container, pipelines);
}
private void ConfigureNHibernateSessionPerRequest(ILifetimeScope container, IPipelines pipelines)
{
pipelines.BeforeRequest += ctx => CreateSession(container);
pipelines.AfterRequest += ctx => CommitSession(container);
pipelines.OnError += (ctx, ex) => RollbackSession(container);
}
private Response CreateSession(ILifetimeScope container)
{
var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
var sessionFactory = provider.Factory;
var requestSession = sessionFactory.OpenSession();
CurrentSessionContext.Bind(requestSession);
requestSession.BeginTransaction();
return null;
}
private AfterPipeline CommitSession(ILifetimeScope container)
{
var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
var sessionFactory = provider.Factory;
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Commit();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
private Response RollbackSession(ILifetimeScope container)
{
var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
var sessionFactory = provider.Factory;
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Rollback();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
My hibernate.cfg.xml looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">...</property>
<property name="show_sql">true</property>
<property name ="current_session_context_class">thread_static</property>
</session-factory>
</hibernate-configuration>
And I wire up the Sessionfactory like this:
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(Assembly.GetExecutingAssembly());
_factory = configuration.BuildSessionFactory();
You can try using AspNetCore middleware since that will give you access to HttpContext.
I found a solution that works, but isn't really as nice as I would have liked it to be.
In my Nancy Bootstrapper I have added a public static property that I populate from the ApplicationStartup hook:
public class Bootstrapper : AutofacNancyBootstrapper
{
public static IHttpContextAccessor HttpContextAccessor { get; private set; }
protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
{
HttpContextAccessor = container.Resolve<IHttpContextAccessor>();
}
}
Then I have created a new custom CurrentSessionContext that I just called CoreSessionContext. It extends the abstract MapBasedSessionContext just like the WebSessionContext does, and then I inject the HttpContextAccessor in the constructor by accessing the static property on the Bootstrapper.
public class CoreSessionContext : MapBasedSessionContext
{
private IHttpContextAccessor _httpContextAccessor;
private const string SessionFactoryMapKey = "NHibernate.Context.WebSessionContext.SessionFactoryMapKey";
public CoreSessionContext(ISessionFactoryImplementor factory) : base(factory)
{
_httpContextAccessor = Bootstrapper.HttpContextAccessor;
}
protected override IDictionary GetMap()
{
return _httpContextAccessor.HttpContext.Items[SessionFactoryMapKey] as IDictionary;
}
protected override void SetMap(IDictionary value)
{
_httpContextAccessor.HttpContext.Items[SessionFactoryMapKey] = value;
}
}
The last thing I did was to remove the current_session_context_class element from the hibernate.cfg.xml file and then wire up the SessionFactory with my custom session context like this in line three:
var configuration = new Configuration();
configuration.Configure();
configuration.CurrentSessionContext<CoreSessionContext>();
configuration.AddAssembly(Assembly.GetExecutingAssembly());
_factory = configuration.BuildSessionFactory();
Then I am able to use the HttpContext provided by AspNetCore.
Not as pretty as I would like it to be, but it works.
The answer to this question explains how to use prototype scope with <rabbit:listener-container/> in Spring AMQP when the listener is not thread-safe.
Another user asked (in a comment) how to configure the same environment using only Java Configuration.
It's generally best practice to use stateless beans for listeners but when that's not possible, to configure #Prototype scope listener (and multiple containers) using only Java Configuration, you can use:
#Bean
public SimpleMessageListenerContainer container1() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
container.setQueueNames("test.mismatch");
container.setMessageListener(new MessageListenerAdapter(listener()));
container.setMismatchedQueuesFatal(true);
return container;
}
...
#Bean
public SimpleMessageListenerContainer containerN() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
container.setQueueNames("test.mismatch");
container.setMessageListener(new MessageListenerAdapter(listener()));
container.setMismatchedQueuesFatal(true);
return container;
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MyNotThreadSafeListener listener() {
return new MyNotThreadSafeListener();
}
Remember that any dependencies injected into MyNotThreadSafeListener must also be prototype beans.
Bottom line is stateless beans are best.
Hi I was trying to define my own rabbitTemplate with autoconfig connctionFactory of spring boot .
first i tried this :
#Configuration
#EnableRabbit
public class MyConfiguration {
#Autowired
private ConnectionFactory connectionFactory;
#Bean
public RabbitTemplate rabbitTemplate(){
RabbitTemplate template = new RabbitTemplate(connectionFactory);
return template;
}
This was not working ...
So I have tried this :
#Configuration
#EnableRabbit
public class MyConfiguration {
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
}
And its works perfectly can someone explain this ?
The problem is that you're injecting a bean reference in your configuration class. That bean reference is something that is provided by auto-config. We can't process that configuration class until those field references are wired. But, we're supposed to "parse" user configuration first (i.e. before auto-configuration kicks in).
This has nothing to do with Spring Boot by the way. You should avoid injecting dependencies like that, especially if these are only required by one #Bean declaration.
In other words, try as much as possible to avoid cycles in your configuration.
I want to send and consume the custom object as below using Spring AMQP.
Producer code
Record record = new Record("message1", new Date());
rabbitTemplate.convertAndSend(record);
Can anyone provide spring amqp #configuration settings for sending and consuming messages as above. Thanks!!!
You should take a look at the Sample Applications; some of them use #Configuration.
But, essentially, you need...
#Bean
public SimpleMessageListenerContainer container() {
SimpleMessageListenerContainer container =
new SimpleMessageListenerContainer(connectionFactory());
MessageListenerAdapter adapter = new MessageListenerAdapter(myListener());
container.setMessageListener(adapter);
container.setQueues(foo());
return container;
}
#Bean
public Object myListener() {
return new Foo();
}
and the listener can be a POJO...
public class Foo {
public void handleMessage(Record foo) {
System.out.println(foo);
}
}
EDIT:
I added a Gist here for XML version.