keycloak-uncached

Details

diff --git a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java
index c01e14b..65946a4 100644
--- a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java
+++ b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java
@@ -100,7 +100,7 @@ public class InfinispanNotificationsManager {
             }
         }
 
-        ExecutorService listenersExecutor = session.getProvider(ExecutorsProvider.class).getExecutor("work-cache-event-listener");
+        ExecutorService listenersExecutor = workRemoteCache==null ? null : session.getProvider(ExecutorsProvider.class).getExecutor("work-cache-event-listener");
         InfinispanNotificationsManager manager = new InfinispanNotificationsManager(workCache, workRemoteCache, myAddress, mySite, listenersExecutor);
 
         // We need CacheEntryListener for communication within current DC
diff --git a/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java b/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java
index 4c6ac3c..aa8f83b 100644
--- a/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java
+++ b/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java
@@ -28,6 +28,10 @@ import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
 import org.keycloak.models.KeycloakSession;
@@ -43,8 +47,15 @@ public class DefaultExecutorsProviderFactory implements ExecutorsProviderFactory
     private int DEFAULT_MIN_THREADS = 4;
     private int DEFAULT_MAX_THREADS = 16;
 
+    private static final String MANAGED_EXECUTORS_SERVICE_JNDI_PREFIX = "java:jboss/ee/concurrency/executor/";
+
+    // Default executor is bound on Wildfly under this name
+    private static final String DEFAULT_MANAGED_EXECUTORS_SERVICE_JNDI = MANAGED_EXECUTORS_SERVICE_JNDI_PREFIX + "default";
+
     private Config.Scope config;
 
+    private Boolean managed = null;
+
     private final Map<String, ExecutorService> executors = new ConcurrentHashMap<>();
 
 
@@ -76,8 +87,11 @@ public class DefaultExecutorsProviderFactory implements ExecutorsProviderFactory
 
     @Override
     public void close() {
-        for (ExecutorService executor : executors.values()) {
-            executor.shutdown();
+        if (managed != null && !managed) {
+            for (Map.Entry<String, ExecutorService> executor : executors.entrySet()) {
+                logger.debugf("Shutting down executor for task '%s'", executor.getKey());
+                executor.getValue().shutdown();
+            }
         }
     }
 
@@ -95,17 +109,7 @@ public class DefaultExecutorsProviderFactory implements ExecutorsProviderFactory
         if (existing == null) {
             synchronized (this) {
                 if (!executors.containsKey(taskType)) {
-                    Config.Scope currentScope = config.scope(taskType);
-                    int min = DEFAULT_MIN_THREADS;
-                    int max = DEFAULT_MAX_THREADS;
-
-                    if (currentScope != null) {
-                        min = currentScope.getInt("min", DEFAULT_MIN_THREADS);
-                        max = currentScope.getInt("max", DEFAULT_MAX_THREADS);
-                    }
-
-                    logger.debugf("Creating pool for task '%s': min=%d, max=%d", taskType, min, max);
-                    ExecutorService executor = createPool(taskType, session, min, max);
+                    ExecutorService executor = retrievePool(taskType, session);
                     executors.put(taskType, executor);
                 }
 
@@ -117,8 +121,82 @@ public class DefaultExecutorsProviderFactory implements ExecutorsProviderFactory
     }
 
 
-    protected ExecutorService createPool(String taskType, KeycloakSession session, int min, int max) {
-        ThreadFactory threadFactory = new ThreadFactory() {
+    protected ExecutorService retrievePool(String taskType, KeycloakSession session) {
+        if (managed == null) {
+            detectManaged();
+        }
+
+        if (managed) {
+            return getPoolManaged(taskType, session);
+        } else {
+            return createPoolEmbedded(taskType, session);
+        }
+    }
+
+    protected void detectManaged() {
+        String jndiName = MANAGED_EXECUTORS_SERVICE_JNDI_PREFIX + "default";
+        try {
+            new InitialContext().lookup(jndiName);
+            logger.debugf("We are in managed environment. Executor '%s' was available.", jndiName);
+            managed = true;
+        } catch (NamingException nnfe) {
+            logger.debugf("We are not in managed environment. Executor '%s' was not available.", jndiName);
+            managed = false;
+        }
+    }
+
+
+    protected ExecutorService getPoolManaged(String taskType, KeycloakSession session) {
+        try {
+            InitialContext ctx = new InitialContext();
+
+            // First check if specific pool for the task
+            String jndiName = MANAGED_EXECUTORS_SERVICE_JNDI_PREFIX + taskType;
+            try {
+                ExecutorService executor = (ExecutorService) ctx.lookup(jndiName);
+                logger.debugf("Found executor for '%s' under JNDI name '%s'", taskType, jndiName);
+                return executor;
+            } catch (NameNotFoundException nnfe) {
+                logger.debugf("Not found executor for '%s' under specific JNDI name '%s'. Fallback to the default pool", taskType, jndiName);
+
+                ExecutorService executor = (ExecutorService) ctx.lookup(DEFAULT_MANAGED_EXECUTORS_SERVICE_JNDI);
+                logger.debugf("Found default executor for '%s' of JNDI name '%s'", taskType, DEFAULT_MANAGED_EXECUTORS_SERVICE_JNDI);
+                return executor;
+            }
+        } catch (NamingException ne) {
+            throw new IllegalStateException(ne);
+        }
+    }
+
+
+    protected ExecutorService createPoolEmbedded(String taskType, KeycloakSession session) {
+        Config.Scope currentScope = config.scope(taskType);
+        int min = DEFAULT_MIN_THREADS;
+        int max = DEFAULT_MAX_THREADS;
+
+        if (currentScope != null) {
+            min = currentScope.getInt("min", DEFAULT_MIN_THREADS);
+            max = currentScope.getInt("max", DEFAULT_MAX_THREADS);
+        }
+
+        logger.debugf("Creating pool for task '%s': min=%d, max=%d", taskType, min, max);
+
+        ThreadFactory threadFactory = createThreadFactory(taskType, session);
+
+        if (min == max) {
+            return Executors.newFixedThreadPool(min, threadFactory);
+        } else {
+            // Same like Executors.newCachedThreadPool. Besides that "min" and "max" are configurable
+            return new ThreadPoolExecutor(min, max,
+                    60L, TimeUnit.SECONDS,
+                    new SynchronousQueue<Runnable>(),
+                    threadFactory);
+        }
+    }
+
+
+    protected ThreadFactory createThreadFactory(String taskType, KeycloakSession session) {
+        return new ThreadFactory() {
 
             private AtomicInteger i = new AtomicInteger(0);
             private int group = new Random().nextInt(2048);
@@ -136,16 +214,6 @@ public class DefaultExecutorsProviderFactory implements ExecutorsProviderFactory
             }
 
         };
-
-        if (min == max) {
-            return Executors.newFixedThreadPool(min, threadFactory);
-        } else {
-            // Same like Executors.newCachedThreadPool. Besides that "min" and "max" are configurable
-            return new ThreadPoolExecutor(min, max,
-                    60L, TimeUnit.SECONDS,
-                    new SynchronousQueue<Runnable>(),
-                    threadFactory);
-        }
     }
 
 }