diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/NodesRegistrationManagement.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/NodesRegistrationManagement.java index 6fab4cf12f..5365620d8b 100644 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/NodesRegistrationManagement.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/NodesRegistrationManagement.java @@ -1,12 +1,15 @@ package org.keycloak.adapters; import java.io.IOException; -import java.util.Timer; -import java.util.TimerTask; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import org.jboss.logging.Logger; -import org.keycloak.enums.RelativeUrlsUsed; import org.keycloak.util.HostUtils; +import org.keycloak.util.Time; /** * @author Marek Posolda @@ -15,76 +18,58 @@ public class NodesRegistrationManagement { private static final Logger log = Logger.getLogger(NodesRegistrationManagement.class); - private final KeycloakDeployment deployment; - private final Timer timer; + private final Map nodeRegistrations = new ConcurrentHashMap(); + private final Executor executor = Executors.newSingleThreadExecutor(); - // True if at least one event was successfully sent - private volatile boolean registered = false; + // Sending registration event during first request to application or if re-registration is needed + public void tryRegister(final KeycloakDeployment resolvedDeployment) { + if (resolvedDeployment.isRegisterNodeAtStartup()) { + final String registrationUri = resolvedDeployment.getRegisterNodeUrl(); + if (needRefreshRegistration(registrationUri, resolvedDeployment)) { + Runnable runnable = new Runnable() { - public NodesRegistrationManagement(KeycloakDeployment deployment) { - this.deployment = deployment; - this.timer = new Timer(); - } - - // Register listener for periodic sending of re-registration event - public void start() { - if (deployment.getRegisterNodePeriod() <= 0) { - log.infof("Skip periodic registration of cluster nodes at startup for application %s", deployment.getResourceName()); - return; - } - - if (deployment.getRelativeUrls() == null || deployment.getRelativeUrls() == RelativeUrlsUsed.ALL_REQUESTS) { - log.errorf("Skip periodic registration of cluster nodes at startup for application %s as Keycloak node can't be contacted. Make sure to provide some non-relative URI in adapters configuration.", deployment.getResourceName()); - return; - } - - addPeriodicListener(); - } - - // Sending registration event during first request to application - public void tryRegister(KeycloakDeployment resolvedDeployment) { - if (resolvedDeployment.isRegisterNodeAtStartup() && !registered) { - synchronized (this) { - if (!registered) { - sendRegistrationEvent(resolvedDeployment); - } + @Override + public void run() { + // Need to check it again in case that executor triggered by other thread already finished computation in the meantime + if (needRefreshRegistration(registrationUri, resolvedDeployment)) { + sendRegistrationEvent(resolvedDeployment); + } + } + }; + executor.execute(runnable); } } } + private boolean needRefreshRegistration(String registrationUri, KeycloakDeployment resolvedDeployment) { + NodeRegistrationContext currentRegistration = nodeRegistrations.get(registrationUri); + /// We don't yet have any registration for this node + if (currentRegistration == null) { + return true; + } + + return currentRegistration.lastRegistrationTime + resolvedDeployment.getRegisterNodePeriod() < Time.currentTime(); + } + + /** + * Called during undeployment or server stop. De-register from all previously registered deployments + */ public void stop() { - removePeriodicListener(); - if (registered) { - sendUnregistrationEvent(); + Collection allRegistrations = nodeRegistrations.values(); + for (NodeRegistrationContext registration : allRegistrations) { + sendUnregistrationEvent(registration.resolvedDeployment); } } - protected void addPeriodicListener() { - TimerTask task = new TimerTask() { - - @Override - public void run() { - sendRegistrationEvent(deployment); - } - }; - - long interval = deployment.getRegisterNodePeriod() * 1000; - log.info("Setup of periodic re-registration event sending each " + interval + " ms"); - timer.schedule(task, interval, interval); - } - - protected void removePeriodicListener() { - timer.cancel(); - } - protected void sendRegistrationEvent(KeycloakDeployment deployment) { log.info("Sending registration event right now"); String host = HostUtils.getIpAddress(); try { ServerRequest.invokeRegisterNode(deployment, host); + NodeRegistrationContext regContext = new NodeRegistrationContext(Time.currentTime(), deployment); + nodeRegistrations.put(deployment.getRegisterNodeUrl(), regContext); log.infof("Node '%s' successfully registered in Keycloak", host); - registered = true; } catch (ServerRequest.HttpFailure failure) { log.error("failed to register node to keycloak"); log.error("status from server: " + failure.getStatus()); @@ -96,7 +81,7 @@ public class NodesRegistrationManagement { } } - protected boolean sendUnregistrationEvent() { + protected boolean sendUnregistrationEvent(KeycloakDeployment deployment) { log.info("Sending Unregistration event right now"); String host = HostUtils.getIpAddress(); @@ -117,4 +102,16 @@ public class NodesRegistrationManagement { } } + public static class NodeRegistrationContext { + + private final Integer lastRegistrationTime; + // deployment instance used for registration request + private final KeycloakDeployment resolvedDeployment; + + public NodeRegistrationContext(Integer lastRegTime, KeycloakDeployment deployment) { + this.lastRegistrationTime = lastRegTime; + this.resolvedDeployment = deployment; + } + } + } diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java index a7a66cf8f5..8b40abb567 100755 --- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java +++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java @@ -127,8 +127,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer(), getController()); setNext(actions); - nodesRegistrationManagement = new NodesRegistrationManagement(kd); - nodesRegistrationManagement.start(); + nodesRegistrationManagement = new NodesRegistrationManagement(); } protected void beforeStop() { diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java index 9465692c1f..94447a1a59 100755 --- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java +++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java @@ -105,8 +105,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer(), getObjectName()); setNext(actions); - nodesRegistrationManagement = new NodesRegistrationManagement(kd); - nodesRegistrationManagement.start(); + nodesRegistrationManagement = new NodesRegistrationManagement(); } protected void beforeStop() { diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java index 24d476b957..5c7a16b4bd 100755 --- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java +++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java @@ -112,7 +112,7 @@ public class KeycloakServletExtension implements ServletExtension { AdapterDeploymentContext deploymentContext = new AdapterDeploymentContext(deployment); servletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext); UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement(); - final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement(deployment); + final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement(); final ServletKeycloakAuthMech mech = createAuthenticationMechanism(deploymentInfo, deploymentContext, userSessionManagement, nodesRegistrationManagement); UndertowAuthenticatedActionsHandler.Wrapper actions = new UndertowAuthenticatedActionsHandler.Wrapper(deploymentContext); diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowNodesRegistrationManagementWrapper.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowNodesRegistrationManagementWrapper.java index 3debc14071..d6ca115880 100644 --- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowNodesRegistrationManagementWrapper.java +++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowNodesRegistrationManagementWrapper.java @@ -18,7 +18,6 @@ public class UndertowNodesRegistrationManagementWrapper implements ServletContex @Override public void contextInitialized(ServletContextEvent sce) { - delegate.start(); } @Override