diff --git a/spring-integration-core/src/main/java/org/springframework/integration/leader/Context.java b/spring-integration-core/src/main/java/org/springframework/integration/leader/Context.java index dbc6990eef..845af65990 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/leader/Context.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/leader/Context.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 the original author or authors. + * Copyright 2014-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.integration.leader; +import org.springframework.lang.Nullable; + /** * Interface that defines the context for candidate leadership. * Instances of this object are passed to {@link Candidate candidates} @@ -27,6 +29,7 @@ package org.springframework.integration.leader; * @author Patrick Peralta * @author Janne Valkealahti * @author Artem Bilan + * @author Gary Russell * */ @FunctionalInterface @@ -49,4 +52,14 @@ public interface Context { // no-op } + /** + * Get the role for the {@link Candidate}. + * @return the role. + * @since 5.0.6 + */ + @Nullable + default String getRole() { + return null; + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/leader/LockRegistryLeaderInitiator.java b/spring-integration-core/src/main/java/org/springframework/integration/support/leader/LockRegistryLeaderInitiator.java index 0acfa4dda0..62dfe16d34 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/leader/LockRegistryLeaderInitiator.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/leader/LockRegistryLeaderInitiator.java @@ -58,6 +58,7 @@ import org.springframework.util.Assert; * @author Vedran Pavic * @author Glenn Renfro * @author Kiel Boatman + * @author Gary Russell * * @since 4.3.1 */ @@ -69,8 +70,6 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe private static final Log logger = LogFactory.getLog(LockRegistryLeaderInitiator.class); - private static final Context NULL_CONTEXT = () -> false; - private final Object lifecycleMonitor = new Object(); /** @@ -87,6 +86,20 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe */ private final Candidate candidate; + private final Context nullContext = new Context() { + + @Override + public boolean isLeader() { + return false; + } + + @Override + public String getRole() { + return LockRegistryLeaderInitiator.this.candidate.getRole(); + } + + }; + /** * Executor service for running leadership daemon. */ @@ -243,11 +256,11 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe } /** - * @return the context (or null if not running) + * @return the context. */ public Context getContext() { if (this.leaderSelector == null) { - return NULL_CONTEXT; + return this.nullContext; } return this.leaderSelector.context; } @@ -520,6 +533,11 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe } } + @Override + public String getRole() { + return LockRegistryLeaderInitiator.this.candidate.getRole(); + } + @Override public String toString() { return "LockContext{role=" + LockRegistryLeaderInitiator.this.candidate.getRole() + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/leader/JdbcLockRegistryLeaderInitiatorTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/leader/JdbcLockRegistryLeaderInitiatorTests.java index 21bdccecfa..e0952cbff0 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/leader/JdbcLockRegistryLeaderInitiatorTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/leader/JdbcLockRegistryLeaderInitiatorTests.java @@ -16,6 +16,7 @@ package org.springframework.integration.jdbc.leader; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -98,7 +99,9 @@ public class JdbcLockRegistryLeaderInitiatorTests { assertNotNull(initiator2); assertThat(initiator1.getContext().isLeader(), is(true)); + assertThat(initiator1.getContext().getRole(), equalTo("bar")); assertThat(initiator2.getContext().isLeader(), is(false)); + assertThat(initiator2.getContext().getRole(), equalTo("bar")); final CountDownLatch granted1 = new CountDownLatch(1); final CountDownLatch granted2 = new CountDownLatch(1); diff --git a/spring-integration-zookeeper/src/main/java/org/springframework/integration/zookeeper/leader/LeaderInitiator.java b/spring-integration-zookeeper/src/main/java/org/springframework/integration/zookeeper/leader/LeaderInitiator.java index 56e93bc3a4..bd464ad640 100644 --- a/spring-integration-zookeeper/src/main/java/org/springframework/integration/zookeeper/leader/LeaderInitiator.java +++ b/spring-integration-zookeeper/src/main/java/org/springframework/integration/zookeeper/leader/LeaderInitiator.java @@ -47,8 +47,6 @@ public class LeaderInitiator implements SmartLifecycle { private static final String DEFAULT_NAMESPACE = "/spring-integration/leader/"; - private static final Context NULL_CONTEXT = () -> false; - private final CuratorContext context = new CuratorContext(); /** @@ -61,6 +59,20 @@ public class LeaderInitiator implements SmartLifecycle { */ private final Candidate candidate; + private final Context nullContext = new Context() { + + @Override + public boolean isLeader() { + return false; + } + + @Override + public String getRole() { + return LeaderInitiator.this.candidate.getRole(); + } + + }; + private final Object lifecycleMonitor = new Object(); /** @@ -203,13 +215,13 @@ public class LeaderInitiator implements SmartLifecycle { } /** - * The context of the initiator or null if not running. - * @return the context (or null if not running) + * The context of the initiator. + * @return the context. * @since 5.0 */ public Context getContext() { if (this.leaderSelector == null) { - return NULL_CONTEXT; + return nullContext; } return this.context; } @@ -292,6 +304,11 @@ public class LeaderInitiator implements SmartLifecycle { LeaderInitiator.this.leaderSelector.interruptLeadership(); } + @Override + public String getRole() { + return LeaderInitiator.this.candidate.getRole(); + } + @Override public String toString() { return "CuratorContext{role=" + LeaderInitiator.this.candidate.getRole() + diff --git a/spring-integration-zookeeper/src/test/java/org/springframework/integration/zookeeper/config/LeaderInitiatorFactoryBeanTests.java b/spring-integration-zookeeper/src/test/java/org/springframework/integration/zookeeper/config/LeaderInitiatorFactoryBeanTests.java index 6e8e440729..766abb30dd 100644 --- a/spring-integration-zookeeper/src/test/java/org/springframework/integration/zookeeper/config/LeaderInitiatorFactoryBeanTests.java +++ b/spring-integration-zookeeper/src/test/java/org/springframework/integration/zookeeper/config/LeaderInitiatorFactoryBeanTests.java @@ -16,6 +16,7 @@ package org.springframework.integration.zookeeper.config; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -91,7 +92,7 @@ public class LeaderInitiatorFactoryBeanTests extends ZookeeperTestSupport { public void testExceptionFromEvent() throws Exception { CountDownLatch onGranted = new CountDownLatch(1); - LeaderInitiator initiator = new LeaderInitiator(client, new DefaultCandidate()); + LeaderInitiator initiator = new LeaderInitiator(client, new DefaultCandidate("foo", "bar")); initiator.setLeaderEventPublisher(new DefaultLeaderEventPublisher() { @@ -107,10 +108,12 @@ public class LeaderInitiatorFactoryBeanTests extends ZookeeperTestSupport { }); + assertThat(initiator.getContext().getRole(), equalTo("bar")); initiator.start(); assertTrue(onGranted.await(10, TimeUnit.SECONDS)); assertTrue(initiator.getContext().isLeader()); + assertThat(initiator.getContext().getRole(), equalTo("bar")); initiator.stop(); } diff --git a/src/reference/asciidoc/endpoint.adoc b/src/reference/asciidoc/endpoint.adoc index 40dac822cd..63f27a6037 100644 --- a/src/reference/asciidoc/endpoint.adoc +++ b/src/reference/asciidoc/endpoint.adoc @@ -704,8 +704,24 @@ An example of this is a file inbound channel adapter that is polling a shared di To participate in a leader election and be notified when elected leader, when leadership is revoked or, failure to acquire the resources to become leader, an application creates a component in the application context called a "leader initiator". Normally a leader initiator is a `SmartLifecycle` so it starts up (optionally) automatically when the context starts, and then publishes notifications when leadership changes. Users can also receive failure notifications by setting the `publishFailedEvents` to `true` (starting with _version 5.0_), in cases when they want take a specific action if a failure occurs. -By convention the user provides a `Candidate` that receives the callbacks and also can revoke the leadership through a `Context` object provided by the framework. -User code can also listen for `org.springframework.integration.leader.event.AbstractLeaderEvent` s, and respond accordingly, for instance using a `SmartLifecycleRoleController`. +By convention, the user provides a `Candidate` that receives the callbacks and also can revoke the leadership through a `Context` object provided by the framework. +User code can also listen for `org.springframework.integration.leader.event.AbstractLeaderEvent` s (the super class of `OnGrantedEvent` and `OnRevokedEvent`), and respond accordingly, for instance using a `SmartLifecycleRoleController`. +The events contain a reference to the `Context` object: + +[source, java] +---- +public interface Context { + + boolean isLeader(); + + void yield(); + + String getRole(); + +} +---- + +Starting with _version 5.0.6_, the context provides a reference to the candidate's role. There is a basic implementation of a leader initiator based on the `LockRegistry` abstraction. To use it you just need to create an instance as a bean, for example: