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());
+    }
+}

Reply via email to