FINERACT-1724: Making global configuration caching transaction bound
This commit is contained in:
committed by
Arnold Gálovics
parent
651052c44b
commit
e9e31a8190
Vendored
+1
-1
@@ -25,6 +25,6 @@ public final class CacheApiConstants {
|
||||
}
|
||||
|
||||
public static final String RESOURCE_NAME = "CACHE";
|
||||
public static final String cacheTypeParameter = "cacheType";
|
||||
public static final String CACHE_TYPE_PARAMETER = "cacheType";
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
public class UpdateCacheCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final CacheWritePlatformService cacheService;
|
||||
private static final Set<String> REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(CacheApiConstants.cacheTypeParameter));
|
||||
private static final Set<String> REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(CacheApiConstants.CACHE_TYPE_PARAMETER));
|
||||
|
||||
@Autowired
|
||||
public UpdateCacheCommandHandler(final CacheWritePlatformService cacheService) {
|
||||
@@ -72,8 +72,8 @@ public class UpdateCacheCommandHandler implements NewCommandSourceHandler {
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(CacheApiConstants.RESOURCE_NAME.toLowerCase());
|
||||
|
||||
final int cacheTypeEnum = command.integerValueSansLocaleOfParameterNamed(CacheApiConstants.cacheTypeParameter);
|
||||
baseDataValidator.reset().parameter(CacheApiConstants.cacheTypeParameter).value(Integer.valueOf(cacheTypeEnum)).notNull()
|
||||
final int cacheTypeEnum = command.integerValueSansLocaleOfParameterNamed(CacheApiConstants.CACHE_TYPE_PARAMETER);
|
||||
baseDataValidator.reset().parameter(CacheApiConstants.CACHE_TYPE_PARAMETER).value(Integer.valueOf(cacheTypeEnum)).notNull()
|
||||
.isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
|
||||
+36
-46
@@ -22,17 +22,17 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.cache.CacheApiConstants;
|
||||
import org.apache.fineract.infrastructure.cache.CacheEnumerations;
|
||||
import org.apache.fineract.infrastructure.cache.data.CacheData;
|
||||
import org.apache.fineract.infrastructure.cache.domain.CacheType;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.jcache.JCacheCacheManager;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -43,49 +43,43 @@ import org.springframework.stereotype.Component;
|
||||
* database on startup and allow user to switch implementation through UI/API
|
||||
*/
|
||||
@Component(value = "runtimeDelegatingCacheManager")
|
||||
public class RuntimeDelegatingCacheManager implements CacheManager {
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class RuntimeDelegatingCacheManager implements CacheManager, InitializingBean {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RuntimeDelegatingCacheManager.class);
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
private final CacheManager noOpCacheManager = new NoOpCacheManager();
|
||||
@Qualifier("ehCacheManager")
|
||||
private final CacheManager ehCacheManager;
|
||||
@Qualifier("defaultCacheManager")
|
||||
private final CacheManager defaultCacheManager;
|
||||
private CacheManager currentCacheManager;
|
||||
|
||||
@Autowired
|
||||
public RuntimeDelegatingCacheManager(final JCacheCacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
this.currentCacheManager = this.noOpCacheManager;
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
currentCacheManager = defaultCacheManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cache getCache(final String name) {
|
||||
return this.currentCacheManager.getCache(name);
|
||||
return currentCacheManager.getCache(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return this.currentCacheManager.getCacheNames();
|
||||
return currentCacheManager.getCacheNames();
|
||||
}
|
||||
|
||||
public Collection<CacheData> retrieveAll() {
|
||||
|
||||
final boolean noCacheEnabled = this.currentCacheManager instanceof NoOpCacheManager;
|
||||
final boolean ehcacheEnabled = this.currentCacheManager instanceof JCacheCacheManager;
|
||||
|
||||
// final boolean distributedCacheEnabled = false;
|
||||
final boolean noCacheEnabled = currentCacheManager == defaultCacheManager;
|
||||
final boolean ehCacheEnabled = currentCacheManager == ehCacheManager;
|
||||
|
||||
final EnumOptionData noCacheType = CacheEnumerations.cacheType(CacheType.NO_CACHE);
|
||||
final EnumOptionData singleNodeCacheType = CacheEnumerations.cacheType(CacheType.SINGLE_NODE);
|
||||
// final EnumOptionData multiNodeCacheType =
|
||||
// CacheEnumerations.cacheType(CacheType.MULTI_NODE);
|
||||
|
||||
final CacheData noCache = CacheData.instance(noCacheType, noCacheEnabled);
|
||||
final CacheData singleNodeCache = CacheData.instance(singleNodeCacheType, ehcacheEnabled);
|
||||
// final CacheData distributedCache =
|
||||
// CacheData.instance(multiNodeCacheType, distributedCacheEnabled);
|
||||
final CacheData singleNodeCache = CacheData.instance(singleNodeCacheType, ehCacheEnabled);
|
||||
|
||||
final Collection<CacheData> caches = Arrays.asList(noCache, singleNodeCache);
|
||||
return caches;
|
||||
return Arrays.asList(noCache, singleNodeCache);
|
||||
}
|
||||
|
||||
public Map<String, Object> switchToCache(final boolean ehcacheEnabled, final CacheType toCacheType) {
|
||||
@@ -93,42 +87,38 @@ public class RuntimeDelegatingCacheManager implements CacheManager {
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
|
||||
final boolean noCacheEnabled = !ehcacheEnabled;
|
||||
final boolean distributedCacheEnabled = !ehcacheEnabled;
|
||||
|
||||
switch (toCacheType) {
|
||||
case INVALID:
|
||||
break;
|
||||
case NO_CACHE:
|
||||
case INVALID -> {
|
||||
log.warn("Invalid cache type used");
|
||||
}
|
||||
case NO_CACHE -> {
|
||||
if (!noCacheEnabled) {
|
||||
changes.put(CacheApiConstants.cacheTypeParameter, toCacheType.getValue());
|
||||
changes.put(CacheApiConstants.CACHE_TYPE_PARAMETER, toCacheType.getValue());
|
||||
}
|
||||
this.currentCacheManager = this.noOpCacheManager;
|
||||
break;
|
||||
case SINGLE_NODE:
|
||||
currentCacheManager = defaultCacheManager;
|
||||
}
|
||||
case SINGLE_NODE -> {
|
||||
if (!ehcacheEnabled) {
|
||||
changes.put(CacheApiConstants.cacheTypeParameter, toCacheType.getValue());
|
||||
changes.put(CacheApiConstants.CACHE_TYPE_PARAMETER, toCacheType.getValue());
|
||||
clearEhCache();
|
||||
}
|
||||
this.currentCacheManager = this.cacheManager;
|
||||
currentCacheManager = ehCacheManager;
|
||||
|
||||
if (this.currentCacheManager.getCacheNames().size() == 0) {
|
||||
LOG.error("No caches configured for activated CacheManager {}", this.currentCacheManager);
|
||||
if (currentCacheManager.getCacheNames().size() == 0) {
|
||||
log.error("No caches configured for activated CacheManager {}", currentCacheManager);
|
||||
}
|
||||
break;
|
||||
case MULTI_NODE:
|
||||
if (!distributedCacheEnabled) {
|
||||
changes.put(CacheApiConstants.cacheTypeParameter, toCacheType.getValue());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MULTI_NODE -> throw new UnsupportedOperationException("Multi node cache is not supported");
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private void clearEhCache() {
|
||||
Iterable<String> cacheNames = cacheManager.getCacheNames();
|
||||
Iterable<String> cacheNames = ehCacheManager.getCacheNames();
|
||||
for (String cacheName : cacheNames) {
|
||||
cacheManager.getCache(cacheName).clear();
|
||||
ehCacheManager.getCache(cacheName).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-26
@@ -19,23 +19,22 @@
|
||||
package org.apache.fineract.infrastructure.configuration.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.cache.domain.CacheType;
|
||||
import org.apache.fineract.infrastructure.cache.domain.PlatformCache;
|
||||
import org.apache.fineract.infrastructure.cache.domain.PlatformCacheRepository;
|
||||
import org.apache.fineract.infrastructure.configuration.data.GlobalConfigurationPropertyData;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.useradministration.domain.Permission;
|
||||
import org.apache.fineract.useradministration.domain.PermissionRepository;
|
||||
import org.apache.fineract.useradministration.exception.PermissionNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ConfigurationDomainServiceJpa implements ConfigurationDomainService {
|
||||
|
||||
public static final String ENABLE_BUSINESS_DATE = "enable_business_date";
|
||||
@@ -55,15 +54,6 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
private final PermissionRepository permissionRepository;
|
||||
private final GlobalConfigurationRepositoryWrapper globalConfigurationRepository;
|
||||
private final PlatformCacheRepository cacheTypeRepository;
|
||||
private static Map<String, GlobalConfigurationPropertyData> configurations = new HashMap<>();
|
||||
|
||||
@Autowired
|
||||
public ConfigurationDomainServiceJpa(final PermissionRepository permissionRepository,
|
||||
final GlobalConfigurationRepositoryWrapper globalConfigurationRepository, final PlatformCacheRepository cacheTypeRepository) {
|
||||
this.permissionRepository = permissionRepository;
|
||||
this.globalConfigurationRepository = globalConfigurationRepository;
|
||||
this.cacheTypeRepository = cacheTypeRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMakerCheckerEnabledForTask(final String taskPermissionCode) {
|
||||
@@ -321,9 +311,7 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
|
||||
@Override
|
||||
public void removeGlobalConfigurationPropertyDataFromCache(final String propertyName) {
|
||||
String identifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier();
|
||||
String key = identifier + "_" + propertyName;
|
||||
configurations.remove(key);
|
||||
globalConfigurationRepository.removeFromCache(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -389,15 +377,8 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
return property.getValue();
|
||||
}
|
||||
|
||||
@Cacheable(value = "configByName", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#propertyName)")
|
||||
public GlobalConfigurationPropertyData getGlobalConfigurationPropertyData(final String propertyName) {
|
||||
String identifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier();
|
||||
String key = identifier + "_" + propertyName;
|
||||
if (!configurations.containsKey(key)) {
|
||||
GlobalConfigurationProperty configuration = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
|
||||
configurations.put(key, configuration.toData());
|
||||
}
|
||||
return configurations.get(key);
|
||||
private GlobalConfigurationPropertyData getGlobalConfigurationPropertyData(final String propertyName) {
|
||||
return globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName).toData();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
@@ -18,8 +18,11 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.configuration.domain;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.configuration.exception.GlobalConfigurationPropertyNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@@ -28,6 +31,7 @@ import org.springframework.stereotype.Service;
|
||||
* </p>
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class GlobalConfigurationRepositoryWrapper {
|
||||
|
||||
private final GlobalConfigurationRepository repository;
|
||||
@@ -37,6 +41,7 @@ public class GlobalConfigurationRepositoryWrapper {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Cacheable(value = "configByName", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#propertyName)")
|
||||
public GlobalConfigurationProperty findOneByNameWithNotFoundDetection(final String propertyName) {
|
||||
final GlobalConfigurationProperty property = this.repository.findOneByName(propertyName);
|
||||
if (property == null) {
|
||||
@@ -61,4 +66,8 @@ public class GlobalConfigurationRepositoryWrapper {
|
||||
this.repository.delete(globalConfigurationProperty);
|
||||
}
|
||||
|
||||
@CacheEvict(value = "configByName", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#propertyName)")
|
||||
public void removeFromCache(String propertyName) {
|
||||
log.debug("Cache entry evicted {}", propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -53,6 +53,7 @@ public class FineractProperties {
|
||||
private FineractJobProperties job;
|
||||
|
||||
private FineractTemplateProperties template;
|
||||
private FineractJpaProperties jpa;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -248,4 +249,10 @@ public class FineractProperties {
|
||||
private List<String> regexWhitelist;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractJpaProperties {
|
||||
|
||||
private boolean statementLoggingEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-3
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.fineract.infrastructure.core.config;
|
||||
package org.apache.fineract.infrastructure.core.config.cache;
|
||||
|
||||
import java.time.Duration;
|
||||
import javax.cache.CacheManager;
|
||||
@@ -28,20 +28,32 @@ import org.ehcache.config.builders.ExpiryPolicyBuilder;
|
||||
import org.ehcache.config.builders.ResourcePoolsBuilder;
|
||||
import org.ehcache.jsr107.Eh107Configuration;
|
||||
import org.springframework.cache.jcache.JCacheCacheManager;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class CacheConfig {
|
||||
|
||||
public static final String CONFIG_BY_NAME_CACHE_NAME = "configByName";
|
||||
|
||||
@Bean
|
||||
public TransactionBoundCacheManager defaultCacheManager(JCacheCacheManager ehCacheManager) {
|
||||
SpecifiedCacheSupportingCacheManager cacheManager = new SpecifiedCacheSupportingCacheManager();
|
||||
cacheManager.setNoOpCacheManager(new NoOpCacheManager());
|
||||
cacheManager.setDelegateCacheManager(ehCacheManager);
|
||||
cacheManager.setSupportedCaches(CONFIG_BY_NAME_CACHE_NAME);
|
||||
return new TransactionBoundCacheManager(cacheManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JCacheCacheManager ehCacheManager() {
|
||||
JCacheCacheManager jCacheCacheManager = new JCacheCacheManager();
|
||||
jCacheCacheManager.setCacheManager(getCustomCacheManager());
|
||||
jCacheCacheManager.setCacheManager(getInternalEhCacheManager());
|
||||
return jCacheCacheManager;
|
||||
}
|
||||
|
||||
private CacheManager getCustomCacheManager() {
|
||||
private CacheManager getInternalEhCacheManager() {
|
||||
CachingProvider provider = Caching.getCachingProvider();
|
||||
CacheManager cacheManager = provider.getCacheManager();
|
||||
|
||||
@@ -61,6 +73,7 @@ public class CacheConfig {
|
||||
cacheManager.createCache("codes", defaultTemplate);
|
||||
cacheManager.createCache("hooks", defaultTemplate);
|
||||
cacheManager.createCache("tfConfig", defaultTemplate);
|
||||
cacheManager.createCache(CONFIG_BY_NAME_CACHE_NAME, defaultTemplate);
|
||||
|
||||
javax.cache.configuration.Configuration<Object, Object> accessTokenTemplate = Eh107Configuration.fromEhcacheCacheConfiguration(
|
||||
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(10000))
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.config.cache;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.jcache.JCacheCacheManager;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SpecifiedCacheSupportingCacheManager implements CacheManager, InitializingBean {
|
||||
|
||||
private JCacheCacheManager delegateCacheManager;
|
||||
private NoOpCacheManager noOpCacheManager;
|
||||
|
||||
private final Set<String> supportedCacheNames = new LinkedHashSet<>(16);
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(delegateCacheManager, "cacheManager cannot be null");
|
||||
Assert.notNull(noOpCacheManager, "delegate cannot be null");
|
||||
Assert.notEmpty(supportedCacheNames, "supportedCacheNames must not be empty");
|
||||
delegateCacheManager.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cache getCache(String name) {
|
||||
if (supportedCacheNames.contains(name)) {
|
||||
Cache cache = delegateCacheManager.getCache(name);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
} else {
|
||||
return noOpCacheManager.getCache(name);
|
||||
}
|
||||
} else {
|
||||
return noOpCacheManager.getCache(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
synchronized (supportedCacheNames) {
|
||||
return Collections.unmodifiableSet(supportedCacheNames);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDelegateCacheManager(JCacheCacheManager delegateCacheManager) {
|
||||
this.delegateCacheManager = delegateCacheManager;
|
||||
}
|
||||
|
||||
public void setNoOpCacheManager(NoOpCacheManager noOpCacheManager) {
|
||||
this.noOpCacheManager = noOpCacheManager;
|
||||
}
|
||||
|
||||
public void setSupportedCaches(String... cacheNames) {
|
||||
supportedCacheNames.addAll(Arrays.asList(cacheNames));
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.config.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.persistence.TransactionLifecycleCallback;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class TransactionBoundCacheManager implements TransactionLifecycleCallback, CacheManager {
|
||||
|
||||
private final CacheManager delegate;
|
||||
|
||||
@Override
|
||||
public void afterCompletion() {
|
||||
Collection<String> cacheNames = delegate.getCacheNames();
|
||||
cacheNames.forEach(c -> {
|
||||
Cache cache = delegate.getCache(c);
|
||||
if (cache != null) {
|
||||
cache.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cache getCache(String name) {
|
||||
return delegate.getCache(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return delegate.getCacheNames();
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.diagnostics.jpa;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import org.eclipse.persistence.internal.jpa.EntityManagerImpl;
|
||||
import org.eclipse.persistence.sessions.changesets.UnitOfWorkChangeSet;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* This class can be used for IntelliJ debugging purposes to access the current transaction bound EntityManager
|
||||
* instance. <br>
|
||||
* <br>
|
||||
* With Alt + F8 you can run evaluations in IntelliJ and this class makes it easier to access the EntityManager and to
|
||||
* see what kind of changes are pending within the Persistence Context. <br>
|
||||
* <br>
|
||||
* To enable this, run Fineract with the <b>diagnostics</b> profile.
|
||||
*/
|
||||
@Profile("diagnostics")
|
||||
@Component
|
||||
public class DiagnosticsEntityManager implements ApplicationContextAware {
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
public static EntityManagerImpl getCurrentEntityManager() {
|
||||
EntityManagerFactory emf = applicationContext.getBean(EntityManagerFactory.class);
|
||||
return (EntityManagerImpl) EntityManagerFactoryUtils.getTransactionalEntityManager(emf);
|
||||
}
|
||||
|
||||
public static UnitOfWorkChangeSet getCurrentChanges() {
|
||||
return DiagnosticsEntityManager.getCurrentEntityManager().getUnitOfWork().getCurrentChanges();
|
||||
}
|
||||
|
||||
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
DiagnosticsEntityManager.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.diagnostics.jpa;
|
||||
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.infrastructure.core.config.jpa.EntityManagerFactoryCustomizer;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.eclipse.persistence.logging.SessionLog;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Conditional(StatementLoggingCustomizerCondition.class)
|
||||
public class StatementLoggingCustomizer implements EntityManagerFactoryCustomizer {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> additionalVendorProperties() {
|
||||
return Map.of("eclipselink.logging.level.sql", SessionLog.FINE_LABEL, PersistenceUnitProperties.LOGGING_PARAMETERS,
|
||||
Boolean.TRUE.toString());
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.diagnostics.jpa;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
|
||||
public class StatementLoggingCustomizerCondition extends PropertiesCondition {
|
||||
|
||||
@Override
|
||||
protected boolean matches(FineractProperties properties) {
|
||||
return properties.getJpa().isStatementLoggingEnabled();
|
||||
}
|
||||
}
|
||||
+21
-1
@@ -18,6 +18,9 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.persistence;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Consumer;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.FlushModeType;
|
||||
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
|
||||
@@ -29,6 +32,8 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
|
||||
public class ExtendedJpaTransactionManager extends JpaTransactionManager {
|
||||
|
||||
private final List<TransactionLifecycleCallback> lifecycleCallbacks = new CopyOnWriteArrayList<>();
|
||||
|
||||
public ExtendedJpaTransactionManager() {
|
||||
setValidateExistingTransaction(true);
|
||||
}
|
||||
@@ -42,7 +47,7 @@ public class ExtendedJpaTransactionManager extends JpaTransactionManager {
|
||||
entityManager.setFlushMode(FlushModeType.COMMIT);
|
||||
}
|
||||
}
|
||||
|
||||
invokeLifecycleCallbacks(TransactionLifecycleCallback::afterBegin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,6 +59,13 @@ public class ExtendedJpaTransactionManager extends JpaTransactionManager {
|
||||
}
|
||||
}
|
||||
super.doCommit(status);
|
||||
invokeLifecycleCallbacks(TransactionLifecycleCallback::afterCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCleanupAfterCompletion(Object transaction) {
|
||||
super.doCleanupAfterCompletion(transaction);
|
||||
invokeLifecycleCallbacks(TransactionLifecycleCallback::afterCompletion);
|
||||
}
|
||||
|
||||
private boolean isReadOnlyTx(Object transaction) {
|
||||
@@ -68,4 +80,12 @@ public class ExtendedJpaTransactionManager extends JpaTransactionManager {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void invokeLifecycleCallbacks(Consumer<TransactionLifecycleCallback> f) {
|
||||
lifecycleCallbacks.forEach(f::accept);
|
||||
}
|
||||
|
||||
public void setLifecycleCallbacks(List<TransactionLifecycleCallback> lifecycleCallbacks) {
|
||||
this.lifecycleCallbacks.addAll(lifecycleCallbacks);
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.persistence;
|
||||
|
||||
public interface TransactionLifecycleCallback {
|
||||
|
||||
default void afterBegin() {}
|
||||
|
||||
default void afterCommit() {}
|
||||
|
||||
default void afterCompletion() {}
|
||||
}
|
||||
+5
-1
@@ -18,7 +18,9 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.jobs;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.persistence.ExtendedJpaTransactionManager;
|
||||
import org.apache.fineract.infrastructure.core.persistence.TransactionLifecycleCallback;
|
||||
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSource;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
|
||||
@@ -39,8 +41,10 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
public class ScheduledJobRunnerConfig {
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
public PlatformTransactionManager transactionManager(ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
|
||||
List<TransactionLifecycleCallback> callbacks) {
|
||||
ExtendedJpaTransactionManager transactionManager = new ExtendedJpaTransactionManager();
|
||||
transactionManager.setLifecycleCallbacks(callbacks);
|
||||
transactionManager.setValidateExistingTransaction(true);
|
||||
transactionManagerCustomizers.ifAvailable(customizers -> customizers.customize(transactionManager));
|
||||
return transactionManager;
|
||||
|
||||
@@ -96,6 +96,8 @@ fineract.template.regex-whitelist=${FINERACT_TEMPLATE_REGEX_WHITELIST:}
|
||||
fineract.report.export.s3.bucket=${FINERACT_REPORT_EXPORT_S3_BUCKET_NAME:}
|
||||
fineract.report.export.s3.enabled=${FINERACT_REPORT_EXPORT_S3_ENABLED:false}
|
||||
|
||||
fineract.jpa.statementLoggingEnabled=${FINERACT_STATEMENT_LOGGING_ENABLED:false}
|
||||
|
||||
# Logging pattern for the console
|
||||
logging.pattern.console=${CONSOLE_LOG_PATTERN:%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(%replace([%X{correlationId}]){'\\[\\]', ''}) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}}
|
||||
|
||||
|
||||
@@ -75,6 +75,8 @@ fineract.content.s3.secretKey=
|
||||
fineract.report.export.s3.bucket=${FINERACT_REPORT_EXPORT_S3_BUCKET_NAME:}
|
||||
fineract.report.export.s3.enabled=${FINERACT_REPORT_EXPORT_S3_ENABLED:false}
|
||||
|
||||
fineract.jpa.statementLoggingEnabled=${FINERACT_STATEMENT_LOGGING_ENABLED:false}
|
||||
|
||||
management.health.jms.enabled=false
|
||||
|
||||
# FINERACT 1296
|
||||
|
||||
Reference in New Issue
Block a user