This is an automated email from the ASF dual-hosted git repository.
pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new b4f41822e KNOX-3156: Ehcache configs not on classpath, fixed
persistence dir lo… (#1053)
b4f41822e is described below
commit b4f41822e545bacc0a057fef16fd80dd656de09e
Author: hanicz <[email protected]>
AuthorDate: Thu Jun 5 20:01:44 2025 +0200
KNOX-3156: Ehcache configs not on classpath, fixed persistence dir lo…
(#1053)
* KNOX-3156: Ehcache configs not on classpath, fixed persistence dir
locking issue
* KNOX-3156: Change lock error message
* KNOX-3156: Check for OverlappingFileLockException for retry
---
.../org/apache/knox/gateway/ShiroMessages.java | 3 +
.../knox/gateway/shirorealm/KnoxCacheManager.java | 68 +++++++++++++++++++++-
.../gateway/shirorealm/KnoxCacheManagerTest.java | 20 ++++++-
3 files changed, 87 insertions(+), 4 deletions(-)
diff --git
a/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/ShiroMessages.java
b/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/ShiroMessages.java
index 28879e724..b68f7a85e 100644
---
a/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/ShiroMessages.java
+++
b/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/ShiroMessages.java
@@ -44,4 +44,7 @@ public interface ShiroMessages {
@Message( level = MessageLevel.WARN, text = "There was an error closing the
CacheManager, reason: {0}" )
void errorClosingManagedCacheManager(@StackTrace(level=MessageLevel.WARN)
Exception e);
+
+ @Message( level = MessageLevel.WARN, text = "The default or configured
ehcache persistence directory is unavailable; Choosing an alternative
directory. reason: {0}" )
+ void resolvePersistenceDirLockError(String cause);
}
diff --git
a/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/shirorealm/KnoxCacheManager.java
b/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/shirorealm/KnoxCacheManager.java
index b5af9fa9d..52db2dd44 100644
---
a/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/shirorealm/KnoxCacheManager.java
+++
b/gateway-provider-security-shiro/src/main/java/org/apache/knox/gateway/shirorealm/KnoxCacheManager.java
@@ -26,14 +26,23 @@ import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.util.Destroyable;
import org.apache.shiro.util.Initializable;
import org.ehcache.CacheManager;
+import org.ehcache.StateTransitionException;
+import org.ehcache.Status;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
+import
org.ehcache.impl.config.persistence.CacheManagerPersistenceConfiguration;
import org.ehcache.integrations.shiro.EhcacheShiro;
+import org.ehcache.spi.service.ServiceCreationConfiguration;
import org.ehcache.xml.XmlConfiguration;
+import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.UUID;
public class KnoxCacheManager implements org.apache.shiro.cache.CacheManager,
Initializable, Destroyable {
private static final ShiroMessages LOG =
MessagesFactory.get(ShiroMessages.class);
@@ -42,6 +51,7 @@ public class KnoxCacheManager implements
org.apache.shiro.cache.CacheManager, In
private String cacheManagerConfigFile =
"classpath:org/ehcache/integrations/shiro/ehcache.xml";
private boolean cacheManagerImplicitlyCreated;
private XmlConfiguration cacheConfiguration;
+ private static final String DEFAULT_FOLDER_NAME = "ehcache-shiro";
public CacheManager getCacheManager() {
return manager;
@@ -99,8 +109,23 @@ public class KnoxCacheManager implements
org.apache.shiro.cache.CacheManager, In
private org.ehcache.CacheManager ensureCacheManager() throws
MalformedURLException {
if (manager == null) {
- manager = CacheManagerBuilder.newCacheManager(getConfiguration());
- manager.init();
+ XmlConfiguration xmlConfiguration = getConfiguration();
+ manager = CacheManagerBuilder.newCacheManager(xmlConfiguration);
+ try {
+ manager.init();
+ } catch (StateTransitionException e) {
+ if(containsOverlappingFileLockException(e)) {
+ LOG.resolvePersistenceDirLockError(e.getMessage());
+ this.resolveLockConflict(xmlConfiguration);
+ if(manager.getStatus() != Status.UNINITIALIZED) {
+ manager.close();
+ }
+ manager = CacheManagerBuilder.newCacheManager(xmlConfiguration);
+ manager.init();
+ } else {
+ throw e;
+ }
+ }
cacheManagerImplicitlyCreated = true;
}
@@ -108,7 +133,44 @@ public class KnoxCacheManager implements
org.apache.shiro.cache.CacheManager, In
return manager;
}
- private URL getResource() {
+ private static boolean containsOverlappingFileLockException(Throwable
throwable) {
+ while (throwable != null) {
+ if (throwable instanceof OverlappingFileLockException) {
+ return true;
+ }
+ throwable = throwable.getCause();
+ }
+ return false;
+ }
+
+ /**
+ * Resolves lock conflicts by changing the persistence directory of the
cache manager.
+ * This is necessary when multiple instances of the cache manager are
created with the same configuration file,
+ * which can lead to lock conflicts.
+ *
+ * @param xmlConfiguration the XML configuration of the cache manager
+ */
+ private void resolveLockConflict(XmlConfiguration xmlConfiguration) {
+ Optional<ServiceCreationConfiguration<?>> serviceConfig =
xmlConfiguration.getServiceCreationConfigurations().stream()
+ .filter(service -> service instanceof
CacheManagerPersistenceConfiguration).findFirst();
+
+ if (serviceConfig.isPresent()) {
+ CacheManagerPersistenceConfiguration cachePersistenceConfig =
(CacheManagerPersistenceConfiguration) serviceConfig.get();
+ String path = cachePersistenceConfig.getRootDirectory().getPath();
+
xmlConfiguration.getServiceCreationConfigurations().remove(cachePersistenceConfig);
+ String newFolder = DEFAULT_FOLDER_NAME +
UUID.randomUUID().toString().substring(0, 4);
+ String newRootDirectory =
Paths.get(path).getParent().resolve(newFolder).toAbsolutePath().toString();
+ xmlConfiguration.getServiceCreationConfigurations()
+ .add(new CacheManagerPersistenceConfiguration(
+ new File(newRootDirectory)));
+ }
+ }
+
+ private URL getResource() throws MalformedURLException {
+ if (this.cacheManagerConfigFile.startsWith("file:")) {
+ return new URL(this.cacheManagerConfigFile);
+ }
+
String URL = ResourceUtils.hasResourcePrefix(this.cacheManagerConfigFile) ?
stripPrefix(this.cacheManagerConfigFile) :
this.cacheManagerConfigFile;
diff --git
a/gateway-provider-security-shiro/src/test/java/org/apache/knox/gateway/shirorealm/KnoxCacheManagerTest.java
b/gateway-provider-security-shiro/src/test/java/org/apache/knox/gateway/shirorealm/KnoxCacheManagerTest.java
index 39d093235..904a8ac32 100644
---
a/gateway-provider-security-shiro/src/test/java/org/apache/knox/gateway/shirorealm/KnoxCacheManagerTest.java
+++
b/gateway-provider-security-shiro/src/test/java/org/apache/knox/gateway/shirorealm/KnoxCacheManagerTest.java
@@ -45,4 +45,22 @@ public class KnoxCacheManagerTest {
assertNull(cacheManager.getCacheManager());
}
-}
\ No newline at end of file
+ @Test
+ public void testMultipleCacheManagersWithSameConfiguration() {
+ KnoxCacheManager cacheManager1 = new KnoxCacheManager();
+ KnoxCacheManager cacheManager2 = new KnoxCacheManager();
+
+ Cache<Object, Object> cache1 = cacheManager1.getCache("cache");
+ Cache<Object, Object> cache2 = cacheManager2.getCache("cache");
+
+ cache1.put("testK", "testV");
+ assertEquals("testV", cache1.get("testK"));
+ cache2.put("testK2", "testV2");
+ assertEquals("testV2", cache2.get("testK2"));
+
+ cacheManager1.destroy();
+ assertNull(cacheManager1.getCacheManager());
+ cacheManager2.destroy();
+ assertNull(cacheManager2.getCacheManager());
+ }
+}