Ehcache - Cannot find cache name for Builder - spring-boot

I've looked through a lot of similar questions asked here but I'm still not able to find a solution so here's my issue:
I'm trying to setup Ehcache on springboot.
Spring 2.2.6.RELEASE
Ehcache 3.8.1
CacheService
I've got a cache named `myCache`.
#Cacheable(value = "myCache")
#GetMapping("/get")
public String get();
CacheConfig
And my config
#Configuration
#EnableCaching
public class CacheConfig {
public CacheConfig() {
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(SimpleKey.class, String.class, ResourcePoolsBuilder.heap(10))).build();
cacheManager.init();
}
}
Error
But I'm getting the following error:
java.lang.IllegalArgumentException: Cannot find cache named 'myCache' for Builder...
I managed to get it to work if I put the config in the xml file, but I rather have it in java.

#Cacheable(value = "myCache") doesn't create a cache named myCache in Ehcache. At runtime, if a cache named myCache is available in Ehcache, it'll use that cache for caching. If not, when attempting to cache at runtime, the exception java.lang.IllegalArgumentException: Cannot find cache named 'myCache' will be thrown. For #Cacheable(value = "myCache") to work with Ehcache as the backend, the cache needs to be created somewhere and Spring needs to be made aware of that cache. The simplest way to do that is to include the spring-boot-starter-cache dependency, add an ehcache.xml with the Ehcache config to classpath and set the config spring.cache.jcache.config: classpath:ehcache.xml in application.yml. You can find a sample application that does that on github
Instead if you do want to configure Ehcache programmatically, you need a org.springframework.cache.CacheManager bean, to initialize the Ehcache config and link it to Spring. The bean definition could look something like below:
import javax.cache.Caching;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public CacheManager ehCacheManager() {
CacheConfiguration<SimpleKey, String> cacheConfig = CacheConfigurationBuilder
.newCacheConfigurationBuilder(SimpleKey.class, String.class, ResourcePoolsBuilder.heap(10))
.build();
javax.cache.CacheManager cacheManager = Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider")
.getCacheManager();
String cacheName = "myCache";
cacheManager.destroyCache(cacheName);
cacheManager.createCache(cacheName, Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfig));
return new JCacheCacheManager(cacheManager);
}
}
Sample working application that configures Ehcache for Spring through code can be found here on github.

Related

Spring Boot: Autowiring app credentials within custom autoconfiguration bean returns null

I am working with a custom AWS Simple System Management client just to avoid the original from using the default AWS authentication chain, so I placed my class in /META-INF/spring.factories and excluded the original from being autconfigured in bootstrap.yml . What I'm facing right now is to get the credentials from application.yml and pass them to my new conf, but when I try to autowire them all I get is null. I wonder if there is any way to achieve it
Here is the code:
package es.example;
import lombok.*;
import org.springframework.boot.context.properties.*;
#ConfigurationProperties(prefix = "aws.credentials")
#Data
public class CustomAWSSSMAuthProperties {
private String accessKey;
private String secretKey;
}
package es.example;
import com.amazonaws.services.simplesystemsmanagement.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.context.properties.*;
#EnableConfigurationProperties(CustomAWSSSMAuthProperties.class)
public class CustomAWSSSMClient extends AWSSimpleSystemsManagementClient {
#Autowired
private CustomAWSSSMAuthProperties customProperties;
public CustomAWSSSMClient() {
String accessKey = customProperties.getAccessKey();
String secretKey = customProperties.getSecretKey();
}
}
/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
es.example.CustomAWSSSMClient
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
es.example.CustomAWSSSMClient
bootstrap.yml
spring:
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
autoconfigure.exclude: com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagementClient
Many thanks
#ConfigurationProperties does not create a bean like #Configuration or #Component does. In your case CustomAWSSSMAuthProperties type bean/object will not be instantiated in the Spring context.
Generally #ConfigurationProperties is used with #Configuration or #Bean to bind the some properties to the bean.
You can annotate CustomAWSSSMAuthProperties with #Configuration to fix the issue.

Why restTemplateBuilder is not loaded in Spring Boot 2.2.4?

I am trying to upgrade SpringBoot from 2.1.1 to 2.2.4.RELEASE.
I found an issue I can't solve.
When I try to run integration tests I run into an error:
Bean method 'restTemplateBuilder' in 'RestTemplateAutoConfiguration' not loaded because NoneNestedConditions 1 matched 0 did not; NestedCondition on RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition.ReactiveWebApplication found ReactiveWebApplicationContext
This is my class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RoutesIT {
I noticed that a new #Conditional(NotReactiveWebApplicationCondition.class) is added to RestTemplateAutoConfiguration.class and this is probably the reason why restTemplateBuilder is not loaded properly.
I can create this bean manually but I don't think that's the best solution.
What should I do to make it work again?
Actually you should use WebClient instead of RestTemplate when you are reactive. See documentation here:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client
But if you have other reasons to keep on using RestTemplate add this class into your test package:
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class TestConfiguration {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
// Need to provide a rest template builder because
// #RestTemplateAutoConfiguration does not work with webflux
return new RestTemplateBuilder();
}
}

Hazelcast client metrics have no value (are always 0.0)

Background / Project Setup:
We are developing a (micro-) service in Springboot (`2.0.4.RELEASE`) with JCache (`javax.cache:cache-api:1.1.0`). We recently switched from Ehcache to Hazelcast (`3.10.4`) to have a central cache cluster for our distributed microservices.
We furthermore use Prometheus (`io.micrometer:micrometer-registry-prometheus:1.0.6`) to export important metrics. After switching, the exported cache metrics do not have any value other than 0.0.
Details:
I use the following Spring configuration for Hazelcast (deleted non-relevant imports)
import org.springframework.cache.CacheManager;
import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.spring.cache.HazelcastCacheManager;
#Configuration
public class HazelcastCacheConfig {
#Bean
public ClientConfig config() {
ClientConfig config = new ClientConfig();
// set group and network config
return config;
}
#Bean
#DependsOn("config")
public HazelcastInstance hazelcastInstance() {
return HazelcastClient.newHazelcastClient(config());
}
#Bean
#DependsOn("hazelcastInstance")
public CacheManager cacheManager() {
return new HazelcastCacheManager(hazelcastInstance());
}
}
Our project requires to create caches dynamically on the fly. So I implemented a custom CacheResolver to create and register these caches and their corresponding metrics:
import org.springframework.boot.actuate.metrics.cache.CacheMetricsRegistrar;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import io.micrometer.core.instrument.binder.cache.HazelcastCacheMetrics;
import io.micrometer.prometheus.PrometheusMeterRegistry;
#Component
public class CacheManagement implements CacheResolver {
#Autowired
CacheManager cacheManager;
#Autowired
CacheMetricsRegistrar cacheMetricsRegistrar;
#Autowired
PrometheusMeterRegistry meterRegistry;
#Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
String cacheName = context.getMethod().getAnnotation(Cacheable.class).cacheNames()[0];
Cache cache = cacheManager.getCache("some Name");
// checks if cache already exists in io.micrometer.prometheus.PrometheusMeterRegistry
if (!cacheRegistered(cache)) {
if (cache.getNativeCache() instanceof IMap<?, ?>)
HazelcastCacheMetrics.monitor(meterRegistry, (IMap<?, ?>) cache.getNativeCache(), /*some tags*/);
// same result with this
// cacheMetricsRegistrar.bindCacheToRegistry(cache, /*some tags*/)
}
}
return cache;
}
Finally I annotate the chacheable methods with
#Cacheable(
cacheNames = "someGeneratedName",
cacheResolver = "cacheManagement",
keyGenerator = "cacheKeyGenerator",
unless = /*..*/,
condition = /*..*/
)
public Object someCacheableMethod(Object... someParameters) {
// logic
}
Now caching works great. The caches are generated at runtime and through debugging I could verify that the caching mechanism works as expected. The metrics are also exported through Prometheus. The only problem is that all caching related metrics always have a value of 0.0.
With debugging I discovered, that the setHits(long hits) method in com.hazelcast.monitor.impl.LocalMapStatsImpl is never called. So when Prometheus scraping leads to getHits() being called, it always returns 0.
What else I tried:
Let Spring generate the CacheManager bean: same result, Spring wraps a HazelcastClientCacheManager in the generated CacheManager bean.
Inject a JCacheCacheManager bean in CacheManagement. Spring still wraps a HazelcastClientCacheManager bean in the JCacheCacheManager but now only JCache cache metrics are exported, none of the Hazelcast specific ones (like cache_partition_gets_total which I thought has to be exported as an alternative to the cache_gets_total{result="miss"} metric according to micrometer issue #586). All values are still 0.0
One last thought/idea that I have is that caching metrics need to enabled on the Hazelcast members somehow but I could not find any information on this.
#steve-mcgarrett, JCache stats in Hazelcast is disabled by default. You need to enable it, either programmatically or adding below config to hazelcast.xml file:
<cache name="default">
<statistics-enabled>false</statistics-enabled>
</cache>
Please see: http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#jcache-declarative-configuration

How to connect to PostgreSQL in Spring Boot 2 using a Bean

I would like to connect to PostgreSQL using a bean (#Bean) and Spring Data.
At this bean, I would like to use my own customer properties in application.properties file, like these properties below
my.db.user=postgres
my.db.password=root
my.db.url=jdbc:postgresql://localhost:5432/mydb
I can't use use default properties (spring.datasource.url , spring.datasource.username, etc .. )
Spring Boot 2 by default uses Hikari to manager connections.
So, you can do it.
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class DatabaseConfig {
#Bean
#Primary
#ConfigurationProperties("spring.datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}
}
application.properties
spring.datasource.platform=postgres
spring.datasource.jdbc-url=jdbc:postgresql://localhost:5432/dbname
spring.datasource.username=postgres
spring.datasource.password=123456
If you wanna use tomcat-jdbc, add the following config.
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
Reference link : https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M2-Release-Notes#default-connection-pool
You can create your own DataSource with your custom database properties the following way.
#Bean
#ConfigurationProperties(prefix="my.db")
public DataSource myDataSource() {
return DataSourceBuilder.create().build();
}
Have a look at the following description to configure two databases for a full example, this explains how you can use your own properties
two databases example

NoUniqueBeanDefinitionException with #EnableExperimentalNeo4jRepositories annotation and SpringBoot 1.4.2

I'm having an issue with Spring boot 1.4.2.M1 and #EnableExperimentalNeo4jRepositories.
It seems to be a conflict between two beans, one spring boot, one spring-data-neo4j.
Here is a stack trace excerpt:
18:12:15.891 [main] DEBUG o.s.b.d.LoggingFailureAnalysisReporter - Application failed to start due to an exception
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.neo4j.ogm.session.Session' available: expected single matching bean but found 2: getSession,org.springframework.data.neo4j.transaction.SharedSessionCreator#0
And another...
Parameter 0 of method setSession in org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean required a single bean, but 2 were found:
- getSession: defined in BeanDefinition defined in class path resource [org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration$SpringBootNeo4jConfiguration.class]
- org.springframework.data.neo4j.transaction.SharedSessionCreator#0: defined by method 'createSharedSession' in null
Anybody have any idea how to solve this?
Below is my Neo4j Configuration
package com.domain.core.context;
import javax.annotation.PostConstruct;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.session.event.Event;
import org.neo4j.ogm.session.event.EventListenerAdapter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.repository.config.EnableExperimentalNeo4jRepositories;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#Configuration
#ComponentScan("com.domain")
#EnableExperimentalNeo4jRepositories(basePackages = "com.domain.core.repository")
#EnableTransactionManagement
#SpringBootApplication(exclude = Neo4jDataAutoConfiguration.class)
public class TestPersistenceContext {
#PostConstruct
public void init() {
log.info("TheScene.Co: Initializing Test Neo4jConfig ...");
}
#Bean
public Neo4jTransactionManager transactionManager() throws Exception {
return new Neo4jTransactionManager(sessionFactory());
}
#Bean
public SessionFactory sessionFactory() {
return new SessionFactory(getConfiguration(), "com.domain") {
#Override
public Session openSession() {
Session session = super.openSession();
session.register(new EventListenerAdapter() {
#Override
public void onPreSave(Event event) {
// do something - like set an id on an object
log.debug("***** Saving domain object ********");
}
});
return session;
}
};
}
#Bean
public org.neo4j.ogm.config.Configuration getConfiguration() {
org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
config.driverConfiguration().setCredentials("neo4j", "password")
.setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver");
return config;
}
}
You must be using Spring Data Neo4j (SDN) version 4.2.0.M1. This milestone release was put out to get feedback on several big changes from 4.1.x.
SDN 4.2.0.RC1 should be out later this week but for now 4.2.0.BUILD-SNAPSHOT is actually quite stable in the lead up to Ingalls release train for Spring Data in Decemeber.
I have written a guide for users coming from SDN 4.0/4.1 which goes over how to upgrade to the snapshot build.
In this guide there is a link to an example project branch which shows how to get this version to work with Spring Boot 1.4.x with a few minor work arounds.
WIth the upcoming release of Spring Boot 1.5, we have updated all the autoconfiguration to work straight out of the box with SDN 4.2. We will update the documenation for Spring Boot closer to release.

Resources