Skip to content

Commit b413e0f

Browse files
garyrussellartembilan
authored andcommitted
INT-4477: Add getRole() to LeaderInitiator Context
JIRA: https://jira.spring.io/browse/INT-4477 When running multiple roles, it is useful to be able to determine the role for a particular context, e.g. in an event. * Docs **cherry-pick to 5.0.x** (cherry picked from commit 73dbdb8)
1 parent 7f898a5 commit b413e0f

File tree

6 files changed

+83
-13
lines changed

6 files changed

+83
-13
lines changed

spring-integration-core/src/main/java/org/springframework/integration/leader/Context.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2017 the original author or authors.
2+
* Copyright 2014-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.integration.leader;
1818

19+
import org.springframework.lang.Nullable;
20+
1921
/**
2022
* Interface that defines the context for candidate leadership.
2123
* Instances of this object are passed to {@link Candidate candidates}
@@ -27,6 +29,7 @@
2729
* @author Patrick Peralta
2830
* @author Janne Valkealahti
2931
* @author Artem Bilan
32+
* @author Gary Russell
3033
*
3134
*/
3235
@FunctionalInterface
@@ -49,4 +52,14 @@ default void yield() {
4952
// no-op
5053
}
5154

55+
/**
56+
* Get the role for the {@link Candidate}.
57+
* @return the role.
58+
* @since 5.0.6
59+
*/
60+
@Nullable
61+
default String getRole() {
62+
return null;
63+
}
64+
5265
}

spring-integration-core/src/main/java/org/springframework/integration/support/leader/LockRegistryLeaderInitiator.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
* @author Vedran Pavic
5959
* @author Glenn Renfro
6060
* @author Kiel Boatman
61+
* @author Gary Russell
6162
*
6263
* @since 4.3.1
6364
*/
@@ -69,8 +70,6 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe
6970

7071
private static final Log logger = LogFactory.getLog(LockRegistryLeaderInitiator.class);
7172

72-
private static final Context NULL_CONTEXT = () -> false;
73-
7473
private final Object lifecycleMonitor = new Object();
7574

7675
/**
@@ -87,6 +86,20 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe
8786
*/
8887
private final Candidate candidate;
8988

89+
private final Context nullContext = new Context() {
90+
91+
@Override
92+
public boolean isLeader() {
93+
return false;
94+
}
95+
96+
@Override
97+
public String getRole() {
98+
return LockRegistryLeaderInitiator.this.candidate.getRole();
99+
}
100+
101+
};
102+
90103
/**
91104
* Executor service for running leadership daemon.
92105
*/
@@ -243,11 +256,11 @@ public void setAutoStartup(boolean autoStartup) {
243256
}
244257

245258
/**
246-
* @return the context (or null if not running)
259+
* @return the context.
247260
*/
248261
public Context getContext() {
249262
if (this.leaderSelector == null) {
250-
return NULL_CONTEXT;
263+
return this.nullContext;
251264
}
252265
return this.leaderSelector.context;
253266
}
@@ -520,6 +533,11 @@ public void yield() {
520533
}
521534
}
522535

536+
@Override
537+
public String getRole() {
538+
return LockRegistryLeaderInitiator.this.candidate.getRole();
539+
}
540+
523541
@Override
524542
public String toString() {
525543
return "LockContext{role=" + LockRegistryLeaderInitiator.this.candidate.getRole() +

spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/leader/JdbcLockRegistryLeaderInitiatorTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.jdbc.leader;
1818

19+
import static org.hamcrest.Matchers.equalTo;
1920
import static org.hamcrest.Matchers.is;
2021
import static org.junit.Assert.assertNotNull;
2122
import static org.junit.Assert.assertThat;
@@ -98,7 +99,9 @@ public void testDistributedLeaderElection() throws Exception {
9899
assertNotNull(initiator2);
99100

100101
assertThat(initiator1.getContext().isLeader(), is(true));
102+
assertThat(initiator1.getContext().getRole(), equalTo("bar"));
101103
assertThat(initiator2.getContext().isLeader(), is(false));
104+
assertThat(initiator2.getContext().getRole(), equalTo("bar"));
102105

103106
final CountDownLatch granted1 = new CountDownLatch(1);
104107
final CountDownLatch granted2 = new CountDownLatch(1);

spring-integration-zookeeper/src/main/java/org/springframework/integration/zookeeper/leader/LeaderInitiator.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ public class LeaderInitiator implements SmartLifecycle {
4747

4848
private static final String DEFAULT_NAMESPACE = "/spring-integration/leader/";
4949

50-
private static final Context NULL_CONTEXT = () -> false;
51-
5250
private final CuratorContext context = new CuratorContext();
5351

5452
/**
@@ -61,6 +59,20 @@ public class LeaderInitiator implements SmartLifecycle {
6159
*/
6260
private final Candidate candidate;
6361

62+
private final Context nullContext = new Context() {
63+
64+
@Override
65+
public boolean isLeader() {
66+
return false;
67+
}
68+
69+
@Override
70+
public String getRole() {
71+
return LeaderInitiator.this.candidate.getRole();
72+
}
73+
74+
};
75+
6476
private final Object lifecycleMonitor = new Object();
6577

6678
/**
@@ -203,13 +215,13 @@ public void setLeaderEventPublisher(LeaderEventPublisher leaderEventPublisher) {
203215
}
204216

205217
/**
206-
* The context of the initiator or null if not running.
207-
* @return the context (or null if not running)
218+
* The context of the initiator.
219+
* @return the context.
208220
* @since 5.0
209221
*/
210222
public Context getContext() {
211223
if (this.leaderSelector == null) {
212-
return NULL_CONTEXT;
224+
return nullContext;
213225
}
214226
return this.context;
215227
}
@@ -292,6 +304,11 @@ public void yield() {
292304
LeaderInitiator.this.leaderSelector.interruptLeadership();
293305
}
294306

307+
@Override
308+
public String getRole() {
309+
return LeaderInitiator.this.candidate.getRole();
310+
}
311+
295312
@Override
296313
public String toString() {
297314
return "CuratorContext{role=" + LeaderInitiator.this.candidate.getRole() +

spring-integration-zookeeper/src/test/java/org/springframework/integration/zookeeper/config/LeaderInitiatorFactoryBeanTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.zookeeper.config;
1818

19+
import static org.hamcrest.Matchers.equalTo;
1920
import static org.hamcrest.Matchers.instanceOf;
2021
import static org.junit.Assert.assertThat;
2122
import static org.junit.Assert.assertTrue;
@@ -91,7 +92,7 @@ public void test() throws Exception {
9192
public void testExceptionFromEvent() throws Exception {
9293
CountDownLatch onGranted = new CountDownLatch(1);
9394

94-
LeaderInitiator initiator = new LeaderInitiator(client, new DefaultCandidate());
95+
LeaderInitiator initiator = new LeaderInitiator(client, new DefaultCandidate("foo", "bar"));
9596

9697
initiator.setLeaderEventPublisher(new DefaultLeaderEventPublisher() {
9798

@@ -107,10 +108,12 @@ public void publishOnGranted(Object source, Context context, String role) {
107108

108109
});
109110

111+
assertThat(initiator.getContext().getRole(), equalTo("bar"));
110112
initiator.start();
111113

112114
assertTrue(onGranted.await(10, TimeUnit.SECONDS));
113115
assertTrue(initiator.getContext().isLeader());
116+
assertThat(initiator.getContext().getRole(), equalTo("bar"));
114117

115118
initiator.stop();
116119
}

src/reference/asciidoc/endpoint.adoc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,24 @@ An example of this is a file inbound channel adapter that is polling a shared di
704704
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".
705705
Normally a leader initiator is a `SmartLifecycle` so it starts up (optionally) automatically when the context starts, and then publishes notifications when leadership changes.
706706
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.
707-
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.
708-
User code can also listen for `org.springframework.integration.leader.event.AbstractLeaderEvent` s, and respond accordingly, for instance using a `SmartLifecycleRoleController`.
707+
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.
708+
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`.
709+
The events contain a reference to the `Context` object:
710+
711+
[source, java]
712+
----
713+
public interface Context {
714+
715+
boolean isLeader();
716+
717+
void yield();
718+
719+
String getRole();
720+
721+
}
722+
----
723+
724+
Starting with _version 5.0.6_, the context provides a reference to the candidate's role.
709725

710726
There is a basic implementation of a leader initiator based on the `LockRegistry` abstraction.
711727
To use it you just need to create an instance as a bean, for example:

0 commit comments

Comments
 (0)