Caffeine Cache with Spring Boot - spring-boot

I have my DAO layers with an expensive method as followed:
#Component
#CacheConfig(cacheNames = {"userStories"})
public class UserStoryDaoImpl implements IUserStoryDao {
#Override
#Cacheable
public List<UserStory> getUserStoriesForProjectAndRelease(UserDto userDto, Set<Integer>
reportProjectId, int releaseId) {
//Slow performing business logic that returns a list
return new ArrayList();
}
and another as
#Component
#CacheConfig(cacheNames = {"features"})
public class FeatureDaoImpl implements IFeatureDao {
#Override
#Cacheable
public List<Features> geFeaturesForProjectAndRelease(UserDto userDto, Set<Integer> reportProjectId,
int releaseId) {
//Slow performing business logic that returns a list
return new ArrayList();
}
}
and my cache config class as :
#Configuration
public class CaffeineCacheConfig {
#Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("features", "userStories");
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
Caffeine< Object, Object > caffeineCacheBuilder() {
return Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(500)
.expireAfterAccess(5, TimeUnit.MINUTES)
.refreshAfterWrite(2, TimeUnit.MINUTES)
.weakKeys()
.recordStats();
}
}
I am using spring boot :: 2.2.6.RELEASE and my pom include :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.8.2</version>
</dependency>
Am I missing something in this? I want to maintain a cache "features" &"userStories" and update it asynchronously after call to the DAO method is made.
I am getting following error :
Cannot load configuration class: com.packageName.CaffeineCacheConfig
Caused by: java.lang.NoClassDefFoundError: com/github/benmanes/caffeine/cache/Caffeine
Caused by: java.lang.ClassNotFoundException: com.github.benmanes.caffeine.cache.Caffeine
I have found these reports related to a similar issue: CaffeineGit-1 and CaffeineGit-Related

Related

Spring Boot ReactiveCircuitBreaker configuration not working

I'm using a circuit breaker implementation in my reactive web service built on Spring Boot Webflux. I'm using below dependencies in pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
</parent>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
<version>2.0.1</version>
</dependency>
And then I created the beans related to Circuit Breaker:
#Configuration
public class NetworkProfileCircuitBreakerConfig {
...
#Bean("networkProfileCircuitBreakerFactory")
public ReactiveCircuitBreakerFactory networkProfileCircuitBreakerFactory() {
return new ReactiveResilience4JCircuitBreakerFactory();
}
#Bean
public ReactiveCircuitBreaker networkProfileCircuitBreaker(#Qualifier("networkProfileCircuitBreakerFactory") ReactiveCircuitBreakerFactory factory) {
return factory.create("networkProfileCircuitBreaker");
}
#Bean
public Customizer<ReactiveResilience4JCircuitBreakerFactory> circuitBreakerCustomizer() {
return factory -> {
factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.circuitBreakerConfig(CircuitBreakerConfig.custom()
.failureRateThreshold(failureRate)
.minimumNumberOfCalls(minimumNumberOfCalls)
.slidingWindowSize(slidingWindowSize)
.enableAutomaticTransitionFromOpenToHalfOpen()
.waitDurationInOpenState(Duration.ofMillis(waitDurationInOpenState))
.ignoreExceptions(BadRequestException.class)
.build())
.timeLimiterConfig(TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(timeLimiter))
.build())
.build());
factory.addCircuitBreakerCustomizer(circuitBreaker -> circuitBreaker.getEventPublisher()
.onStateTransition(e -> {
switch(e.getStateTransition().getToState()) {
case CLOSED:
log.info("Circuit Breaker is now CLOSED.");
break;
case HALF_OPEN:
log.info("Circuit Breaker is now HALF_OPEN.");
break;
case OPEN:
log.info("Circuit Breaker is now OPEN!");
break;
case METRICS_ONLY:
break;
default:
break;
}
}), "circuitBreakerStateTransitionEvents");
};
}
}
Then I autowired the ReactiveCircuitBreaker bean to my service in order to use it in my reactive call:
...
#Service
public class NetworkProfileService {
...
#Autowired
private ReactiveCircuitBreaker networkProfileCircuitBreaker;
...
public Mono<ResponseEntity<NetworkProfileResponse>> getNetworkProfile(NetworkProfileRequest request) {
return networkProfileCircuitBreaker.run(adapter.getData(request)
, throwable -> {
//Fallback method
});
}
}
However, it seems like my ReactiveResilience4JCircuitBreakerFactory is not working properly; the circuit breaker seems to be using default settings instead of my customized settings.
I have tried may things including moving the Factory to my Service class constructor; to no avail.
Is there anything that I might have missed?
I had the same issue, my workaround was to do it manually (in buildConfiguration method you can add the config):
#Bean
public ReactiveCircuitBreaker reactiveCircuitBreaker(ReactiveCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory){
return reactiveResilience4JCircuitBreakerFactory.create("id");
}
#Bean
public ReactiveCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory() {
ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory = new ReactiveResilience4JCircuitBreakerFactory();
reactiveResilience4JCircuitBreakerFactory.configureDefault(buildConfiguration());
return reactiveResilience4JCircuitBreakerFactory;
}
private Function<String, Resilience4JCircuitBreakerConfiguration> buildConfiguration() {
Resilience4JCircuitBreakerConfiguration resilience4JCircuitBreakerConfiguration = new Resilience4JCircuitBreakerConfiguration();
CircuitBreakerConfig breakerConfig = CircuitBreakerConfig
.from(CircuitBreakerConfig.ofDefaults())
.build();
resilience4JCircuitBreakerConfiguration.setCircuitBreakerConfig(breakerConfig);
resilience4JCircuitBreakerConfiguration.setTimeLimiterConfig(TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(TIMEOUT)).build());
return (id -> resilience4JCircuitBreakerConfiguration);
}

Persistent aware KieSession not using Pessimistic Lock during transactions

I am using Drools with Spring Boot 2.3 and I have implemented the persistent aware KieSession, in which MySQL is used for storing the session. I have successfully integrated the default EntityManagerFactory of Spring Boot with Drools but my problem is with transactions. By default, Drools uses Optimistic Lock during transactions but it allows us to use the Pessimistic Lock as well, which is what I want. Now while firing rules, Drools persists/updates the KieSession in MySQL with the following query:
update SessionInfo set lastModificationDate=?, rulesByteArray=?, startDate=?, OPTLOCK=? where id=? and OPTLOCK=?
Now the above statement is executed twice if I do not use transactions using the #Transactional annotation in the method, and if #Transactional is used then the above statement is executed only once after firing the rules.
Now, if I manually change the value of the OPTLOCK field then Drools throws an exception:
javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.drools.persistence.info.SessionInfo#1]
followed by:
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.drools.persistence.info.SessionInfo#1]
I am unable to post the entire Stacktrace due to text length limitations here. The entire stacktrace can be viewed in this GitHub project.
I am not sure whether Drools is using the Pessimistic Lock as defined in the environment. About my session implementation, I want to have a single KieSession since I am using KieSession as a Bean.
Below is my implementation:
The configuration class:
#Configuration
public class DynamicDroolsConfig {
private KieServices kieServices;
private KieFileSystem kieFileSystem;
#Autowired
private PersistentSessionDAO persistentSessionDAO;
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
#Autowired
private PlatformTransactionManager platformTransactionManager;
#PostConstruct
private void init() {
this.kieServices = KieServices.Factory.get();
this.kieFileSystem = kieServices.newKieFileSystem();
}
#Bean
public KieServices getKieServices() {
return this.kieServices;
}
#Bean
public KieContainer getKieContainer() {
kieFileSystem.write(ResourceFactory.newClassPathResource("rules/rules.drl"));
final KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
KieBuilder kb = kieServices.newKieBuilder(kieFileSystem).buildAll();
KieModule kieModule = kb.getKieModule();
return kieServices.newKieContainer(kieModule.getReleaseId());
}
#Bean
public KieFileSystem getFileSystem() {
return kieFileSystem;
}
#Bean
public KieSession kieSession() {
List<SessionInfo> sessionDetails = persistentSessionDAO.getSessionDetails();
if (sessionDetails.size() == 0) {
return kieServices.getStoreServices().newKieSession(getKieContainer().getKieBase(), null, getEnv());
} else {
return kieServices.getStoreServices().loadKieSession(sessionDetails.get(0).getId(), getKieContainer().getKieBase(), null, getEnv());
}
}
private Environment getEnv() {
Environment env = kieServices.newEnvironment();
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);
env.set(EnvironmentName.TRANSACTION_MANAGER, platformTransactionManager);
env.set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true);
env.set(EnvironmentName.USE_PESSIMISTIC_LOCKING_MODE, LockModeType.PESSIMISTIC_FORCE_INCREMENT.name());
return env;
}
}
The controller class:
#RestController
public class MyController {
#Autowired
private KieSession kieSession;
#Transactional
#GetMapping("fire-person")
public void firePerson() {
Person person = new Person();
person.setName("Christy");
kieSession.insert(person);
kieSession.fireAllRules();
}
}
The Fact class
public class Person implements Serializable {
private String name;
private int age;
private String gender;
private String toCompareName;
private String toCompareGender;
// getters and setters
}
The repository interface:
public interface DroolsSessionRepository extends JpaRepository<SessionInfo, Long> {
}
The service class:
#Service
public class PersistentSessionDAO {
#Autowired
private DroolsSessionRepository droolsSessionRepository;
public List<SessionInfo> getSessionDetails() {
return droolsSessionRepository.findAll();
}
}
The runner class:
#EntityScan(basePackages = {"com.sam.springdroolspersistence.entity", "org.drools.persistence.info"})
#EnableJpaRepositories
#SpringBootApplication
public class SpringDroolsPersistenceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDroolsPersistenceApplication.class, args);
}
}
The Drools dependencies used:
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-persistence-jpa</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-persistence-jpa</artifactId>
<version>${drools-version}</version>
</dependency>
The code implementation can also be found in this GitHub Project. Any kind of help/suggestions will be much appreciated. Thank you.
Pessimistic locking is implemented only in JBPM see here
There's no such functionality in Drools persistence, SessionInfo will always use OptimisticLocking based on JPA's #Version annotation.
If you need such feature, please file a feature request on Drools' Jira

Setting up basic Spring AOP configuration

I'm learning Spring's AOP, following this tutorial, but somehow I cannot make it work. The only modification I made, I hope, is to make it work with a cotroller.
directory structure
-/java
-/com.example.spring_aop
-/aspects
-LoggingAspect.java
-/controllers
-AddController.java
-SpringAopApplication.java
-/resources
-beans.xml
AddController.java
#RestController
public class AddController {
#GetMapping(value = "/{a}+{b}", produces = MediaType.APPLICATION_JSON_VALUE)
public Map add(#PathVariable("a") int a, #PathVariable("b") int b) {
int result = a+b;
return Collections.singletonMap("result", result);
}
}
LoggingAspect.java
#Component
#Aspect
public class LoggingAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Before("execution( * com.example.spring_aop.controllers..*.*(..) )")
public void before(JoinPoint joinPoint) {
logger.info(" ###### before executing method: {} of class: {}",
joinPoint.getSignature().getName(), joinPoint.getTarget().getClass().getName());
}
#AfterReturning(pointcut = "execution( * com.example.spring_aop.controllers..*.*(..) )",
returning = "result")
public void after(Object result) {
logger.info(" ###### method returned: {}", result);
}
}
pom.xml
Spring Web auto-generated (Spring Initializr) pom with one added dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Solved it by choosing annotation-based configuration over xml. The AspectJ annotations present in my question are actually sufficient, so I just deleted the beans.xml config and it works as expected.

Spring Data Mongodb WriteConcern working?

I have the following Spring Data MongoDb Repository Java configuration:
#EnableMongoRepositories(basePackages= {"com.example.repositories.mongodb"})
public class MongoConfig extends AbstractMongoConfiguration {
private #Value("${mongo.host}") String mongoHost;
private #Value("${mongo.port}") int mongoPort;
private #Value("${mongo.database}") String mongoDatabase;
#Override
protected String getDatabaseName() {
return mongoDatabase;
}
#Override
public Mongo mongo() throws Exception {
MongoClientOptions options = MongoClientOptions.builder()
.connectionsPerHost(100)
.connectTimeout(120000)
.socketTimeout(120000)
.maxWaitTime(1200000)
.threadsAllowedToBlockForConnectionMultiplier(1500)
.writeConcern(WriteConcern.ACKNOWLEDGED)
.build();
MongoClient client = new MongoClient(new ServerAddress(mongoHost, mongoPort), options);
return client;
}
public #Bean PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
I am trying to figure out if the writeConcern is really turned on due to some jUnits not working when running repeatedly. If I place a breakpoint after the client is created above and inspect the client object I can see its property WriteConcern equals:
{w=null, wTimeout=null ms, fsync=null, journal=null}
Which suggests to me that it was not set to ACKNOWLEDGED.
Am I setting it properly and is there a way to see if the correct concern is set? None of the logger options I tried made it output.
My dependencies are:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>${spring.data.mongodb}</version>
</dependency>

Making Aspectj work on a Spring servlet bean

I am trying to get an aspectprofiler working on a Jersey servlet registered in a spring project. The aspectprofiler is loaded, but don't notice when methods within the Jersey servlet are run.
#EnableAutoConfiguration
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class App {
public static void main(final String[] args) {
final SpringApplicationBuilder sab = new SpringApplicationBuilder(ConsolidatedCustomerMasterApp.class);
sab.run(args);
}
#Bean
public ServletRegistrationBean jerseyServlet() {
final ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyInitialization.class.getName());
return registration;
}
#Bean
public AspectProfiler profiler() {
return new AspectProfiler();
}
}
...
public class JerseyInitialization extends ResourceConfig {
public JerseyInitialization() {
packages("com.example.package");
}
...
package com.example.package;
//imports
#Path("/test")
public class RestService {
#GET
#Path("test")
#Produces(MediaType.TEXT_PLAIN)
public String test() {
return "Something";
}
}
...
#Aspect
public class AspectProfiler {
private static final DefaultApplicationProfiler PROFILER = new DefaultApplicationProfiler(
Arrays.<ProfilerOperator> asList(
new StatsdProfilerOperator(),
new LoggingProfilerOperator())
);
private static final String REST_MATCHER =
"execution(* com.example.package..*.*(..))";
#Around(REST_MATCHER)
public Object around(final ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("test");
return PROFILER.around(joinPoint);
}
}
On top of making the Jersey resource classes Spring #Components (and #ComponentScaning for them), you also need to make the ResourceConfig a Spring #Component also. You can see in the Spring Boot JerseyAutoConfigurer that it autowires the ResourceConfig, which it uses for the ServletContainer registration.
One thing to also note is that it creates its own ServletRegistrationBean
public ServletRegistrationBean jerseyServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(
new ServletContainer(this.config), this.path);
addInitParameters(registration);
registration.setName("jerseyServlet");
return registration;
}
When you declare your own, you are overriding this one. You are not adding any special functionality that is not already provided, so just leave the default. Any Jersey specific configurations can be added in the application.properties file or through code configurations.
As for dependencies, I'll just assume you have all the right dependencies. The following are what I used to test
<!-- all 1.2.7.RELEASE -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
See Also:
spring-boot-sample-jersey - from project samples
ยง26.2 JAX-RS and Jersey - from Spring Boot docs

Resources