From 904ed9abb0b60d967370796f569b6fe9e1bf6e8b Mon Sep 17 00:00:00 2001 From: slaurenz <82034561+slaurenz@users.noreply.github.com> Date: Mon, 27 Sep 2021 14:54:28 +0200 Subject: [PATCH] feat: in-memory cache (#38) * Add signature creation for empty list responses * Added caching to services * Checkstyle fixes * Fixed JUnit tests --- .../DgcBusinessRuleServiceApplication.java | 2 ++ .../service/BusinessRuleService.java | 27 +++++++++++++++---- .../service/CountryListService.java | 22 +++++++-------- .../businessrule/service/ValueSetService.java | 21 +++++++++++++-- ...BusinessRuleControllerIntegrationTest.java | 10 +++++++ .../CountryListControllerIntegrationTest.java | 4 +++ .../ValueSetControllerIntegrationTest.java | 8 +++++- .../testdata/BusinessRulesTestHelper.java | 8 ++++++ 8 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/main/java/eu/europa/ec/dgc/businessrule/DgcBusinessRuleServiceApplication.java b/src/main/java/eu/europa/ec/dgc/businessrule/DgcBusinessRuleServiceApplication.java index 1028ac0..4025203 100644 --- a/src/main/java/eu/europa/ec/dgc/businessrule/DgcBusinessRuleServiceApplication.java +++ b/src/main/java/eu/europa/ec/dgc/businessrule/DgcBusinessRuleServiceApplication.java @@ -26,11 +26,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.cache.annotation.EnableCaching; /** * The Application class. */ @SpringBootApplication +@EnableCaching @EnableConfigurationProperties({DgcConfigProperties.class, JksSigningConfig.class}) public class DgcBusinessRuleServiceApplication extends SpringBootServletInitializer { diff --git a/src/main/java/eu/europa/ec/dgc/businessrule/service/BusinessRuleService.java b/src/main/java/eu/europa/ec/dgc/businessrule/service/BusinessRuleService.java index 37fc053..242eb1e 100644 --- a/src/main/java/eu/europa/ec/dgc/businessrule/service/BusinessRuleService.java +++ b/src/main/java/eu/europa/ec/dgc/businessrule/service/BusinessRuleService.java @@ -35,8 +35,11 @@ import java.util.Locale; import java.util.Optional; import java.util.stream.Collectors; +import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,36 +56,50 @@ public class BusinessRuleService { private final BusinessRulesUtils businessRulesUtils; + /** + * Creates the signature for the empty rules list after start up. + */ + @PostConstruct + @Transactional + public void businessRuleServiceInit() { + listSigningService.updateSignedList(getBusinessRulesList(),ListType.Rules); + } + /** * Gets list of all business rules ids and hashes. * */ + @Cacheable("business_rules") public List getBusinessRulesList() { - + log.debug("Get Rules list executed."); List rulesItems = businessRuleRepository.findAllByOrderByIdentifierAsc(); return rulesItems; } + @Cacheable("business_rules") public Optional getBusinessRulesSignedList() { + log.debug("Get Rules list executed."); return signedListRepository.findById(ListType.Rules); } /** * Gets list of all business rules ids and hashes for a country. */ + @Cacheable("business_rules") public List getBusinessRulesListForCountry(String country) { - + log.debug("Get Rules list for country ({}) executed.", country); List rulesItems = businessRuleRepository.findAllByCountryOrderByIdentifierAsc(country.toUpperCase(Locale.ROOT)); return rulesItems; } /**f - * Gets a business rule by hash. + * Gets a business rule by country and hash. */ @Transactional + @Cacheable("business_rules") public BusinessRuleEntity getBusinessRuleByCountryAndHash(String country, String hash) { - + log.debug("Get rule for country ({}) and hash ({}) executed.", country, hash); return businessRuleRepository.findOneByCountryAndHash(country, hash); } @@ -91,6 +108,7 @@ public BusinessRuleEntity getBusinessRuleByCountryAndHash(String country, String * @param businessRules list of actual value sets */ @Transactional + @CacheEvict(value = "business_rules", allEntries = true) public void updateBusinessRules(List businessRules) { List ruleHashes = businessRules.stream().map(BusinessRuleItem::getHash).collect(Collectors.toList()); @@ -154,7 +172,6 @@ public List createBusinessRuleItemList(List va return businessRuleItems; } - /** * Gets a list of hash values of all stored business rules. * @return List of hash values diff --git a/src/main/java/eu/europa/ec/dgc/businessrule/service/CountryListService.java b/src/main/java/eu/europa/ec/dgc/businessrule/service/CountryListService.java index e65a11f..0a9dcbe 100644 --- a/src/main/java/eu/europa/ec/dgc/businessrule/service/CountryListService.java +++ b/src/main/java/eu/europa/ec/dgc/businessrule/service/CountryListService.java @@ -27,6 +27,8 @@ import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -46,10 +48,13 @@ public class CountryListService { * @return the country list. */ @Transactional + @Cacheable("country_list") public CountryListEntity getCountryList() { + log.debug("Get country list executed"); CountryListEntity cle = countryListRepository.getFirstById(COUNTRY_LIST_ID); if (cle == null) { - cle = new CountryListEntity(COUNTRY_LIST_ID,"[]",null,null); + cle = createCountryListEntity("[]"); + countryListRepository.save(cle); } return cle; } @@ -60,20 +65,16 @@ public CountryListEntity getCountryList() { * @param newCountryListData new country list data */ @Transactional + @CacheEvict(value = "country_list", allEntries = true) public void updateCountryList(String newCountryListData) { CountryListEntity oldList = getCountryList(); if (!newCountryListData.equals(oldList.getRawData())) { - saveCountryList(newCountryListData); + countryListRepository.save(createCountryListEntity(newCountryListData)); } } - /** - * Saves a country list by replacing an old one. - * @param listData the country list to be saved. - */ - @Transactional - public void saveCountryList(String listData) { + private CountryListEntity createCountryListEntity(String listData) { CountryListEntity cle = new CountryListEntity(COUNTRY_LIST_ID,listData,null,null); try { cle.setHash(businessRulesUtils.calculateHash(listData)); @@ -83,10 +84,7 @@ public void saveCountryList(String listData) { if (signingService.isPresent()) { cle.setSignature(signingService.get().computeSignature(cle.getHash())); } - countryListRepository.save(cle); + return cle; } - - - } diff --git a/src/main/java/eu/europa/ec/dgc/businessrule/service/ValueSetService.java b/src/main/java/eu/europa/ec/dgc/businessrule/service/ValueSetService.java index d6f8167..17f785f 100644 --- a/src/main/java/eu/europa/ec/dgc/businessrule/service/ValueSetService.java +++ b/src/main/java/eu/europa/ec/dgc/businessrule/service/ValueSetService.java @@ -34,8 +34,11 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -51,16 +54,28 @@ public class ValueSetService { private final SignedListRepository signedListRepository; private final Optional signingService; + /** + * Creates the signature for the empty value sets list after start up. + */ + @PostConstruct + @Transactional + public void valueSetServiceInit() { + listSigningService.updateSignedList(getValueSetsList(), ListType.ValueSets); + } + /** * Gets list of all value set ids and hashes. */ + @Cacheable("value_sets") public List getValueSetsList() { - + log.debug("Get value sets list executed"); List valueSetItems = valueSetRepository.findAllByOrderByIdAsc(); return valueSetItems; } + @Cacheable("value_sets") public Optional getValueSetsSignedList() { + log.debug("Get value sets list (SignedList) executed"); return signedListRepository.findById(ListType.ValueSets); } @@ -69,8 +84,9 @@ public Optional getValueSetsSignedList() { * Gets a value set by its hash value. */ @Transactional + @Cacheable("value_sets") public ValueSetEntity getValueSetByHash(String hash) { - + log.debug("Get value set ({})executed", hash); return valueSetRepository.findOneByHash(hash); } @@ -79,6 +95,7 @@ public ValueSetEntity getValueSetByHash(String hash) { * @param valueSets list of actual value sets */ @Transactional + @CacheEvict(value = "value_sets", allEntries = true) public void updateValueSets(List valueSets) { List valueSetsHashes = valueSets.stream().map(ValueSetItem::getHash).collect(Collectors.toList()); List alreadyStoredValueSets = getValueSetsHashList(); diff --git a/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/BusinessRuleControllerIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/BusinessRuleControllerIntegrationTest.java index 5b6bf54..d0aec2d 100644 --- a/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/BusinessRuleControllerIntegrationTest.java +++ b/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/BusinessRuleControllerIntegrationTest.java @@ -1,6 +1,7 @@ package eu.europa.ec.dgc.businessrule.restapi.controller; import eu.europa.ec.dgc.businessrule.repository.BusinessRuleRepository; +import eu.europa.ec.dgc.businessrule.service.BusinessRuleService; import eu.europa.ec.dgc.businessrule.testdata.BusinessRulesTestHelper; import eu.europa.ec.dgc.gateway.connector.DgcGatewayCountryListDownloadConnector; import eu.europa.ec.dgc.gateway.connector.DgcGatewayValidationRuleDownloadConnector; @@ -11,6 +12,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.cache.CacheManager; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -35,16 +37,24 @@ class BusinessRuleControllerIntegrationTest { @Autowired BusinessRuleRepository businessRuleRepository; + @Autowired + BusinessRuleService businessRuleService; + @Autowired BusinessRulesTestHelper businessRulesTestHelper; @Autowired private MockMvc mockMvc; + @Autowired + CacheManager cacheManager; @BeforeEach void clearRepositoryData() { + businessRuleRepository.deleteAll(); + cacheManager.getCache("business_rules").clear(); + businessRuleService.businessRuleServiceInit(); } @Test diff --git a/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/CountryListControllerIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/CountryListControllerIntegrationTest.java index 038d8d8..c993f55 100644 --- a/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/CountryListControllerIntegrationTest.java +++ b/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/CountryListControllerIntegrationTest.java @@ -31,6 +31,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.cache.CacheManager; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -62,11 +63,14 @@ class CountryListControllerIntegrationTest { @BeforeEach void clearRepositoryData() { countryListRepository.deleteAll(); + cacheManager.getCache("country_list").clear(); } @Autowired private MockMvc mockMvc; + @Autowired + CacheManager cacheManager; @Test void getEmptyCountryList() throws Exception { diff --git a/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/ValueSetControllerIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/ValueSetControllerIntegrationTest.java index 2154a7e..a37bd18 100644 --- a/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/ValueSetControllerIntegrationTest.java +++ b/src/test/java/eu/europa/ec/dgc/businessrule/restapi/controller/ValueSetControllerIntegrationTest.java @@ -38,6 +38,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.cache.CacheManager; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -78,10 +79,15 @@ class ValueSetControllerIntegrationTest { @Autowired private SignedListRepository signedListRepository; + @Autowired + CacheManager cacheManager; + @BeforeEach void clearRepositoryData() { valueSetRepository.deleteAll(); signedListRepository.deleteAll(); + cacheManager.getCache( "value_sets").clear(); + valueSetService.valueSetServiceInit(); } @Test @@ -110,7 +116,7 @@ void getValueSetList() throws Exception { BusinessRulesTestHelper.VALUESET_IDENTIFIER_2, BusinessRulesTestHelper.VALUESET_DATA_2); - listSigningService.updateSignedList(valueSetService.getValueSetsList(), ListType.ValueSets); + mockMvc.perform(get("/valuesets").header(API_VERSION_HEADER, "1.0")) .andExpect(status().isOk()) diff --git a/src/test/java/eu/europa/ec/dgc/businessrule/testdata/BusinessRulesTestHelper.java b/src/test/java/eu/europa/ec/dgc/businessrule/testdata/BusinessRulesTestHelper.java index f041d8e..fa9246e 100644 --- a/src/test/java/eu/europa/ec/dgc/businessrule/testdata/BusinessRulesTestHelper.java +++ b/src/test/java/eu/europa/ec/dgc/businessrule/testdata/BusinessRulesTestHelper.java @@ -4,6 +4,8 @@ import eu.europa.ec.dgc.businessrule.entity.ValueSetEntity; import eu.europa.ec.dgc.businessrule.repository.BusinessRuleRepository; import eu.europa.ec.dgc.businessrule.repository.ValueSetRepository; +import eu.europa.ec.dgc.businessrule.service.BusinessRuleService; +import eu.europa.ec.dgc.businessrule.service.ValueSetService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -163,6 +165,10 @@ public class BusinessRulesTestHelper { private final BusinessRuleRepository businessRuleRepository; + private final BusinessRuleService businessRuleService; + + private final ValueSetService valueSetService; + public void insertBusinessRule(String hash, String identifier, String country, String version, String data) { BusinessRuleEntity bre = new BusinessRuleEntity(); bre.setHash(hash); @@ -172,6 +178,7 @@ public void insertBusinessRule(String hash, String identifier, String country, S bre.setRawData(data); businessRuleRepository.save(bre); + businessRuleService.businessRuleServiceInit(); } @@ -182,6 +189,7 @@ public void insertValueSet(String hash, String identifier, String data) { vse.setRawData(data); valueSetRepository.save(vse); + valueSetService.valueSetServiceInit(); } }