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**
This commit is contained in:
committed by
Artem Bilan
parent
c9faf3c8ce
commit
73dbdb80be
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.integration.leader;
|
package org.springframework.integration.leader;
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that defines the context for candidate leadership.
|
* Interface that defines the context for candidate leadership.
|
||||||
* Instances of this object are passed to {@link Candidate candidates}
|
* Instances of this object are passed to {@link Candidate candidates}
|
||||||
@@ -27,6 +29,7 @@ package org.springframework.integration.leader;
|
|||||||
* @author Patrick Peralta
|
* @author Patrick Peralta
|
||||||
* @author Janne Valkealahti
|
* @author Janne Valkealahti
|
||||||
* @author Artem Bilan
|
* @author Artem Bilan
|
||||||
|
* @author Gary Russell
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
@@ -49,4 +52,14 @@ public interface Context {
|
|||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the role for the {@link Candidate}.
|
||||||
|
* @return the role.
|
||||||
|
* @since 5.0.6
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default String getRole() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import org.springframework.util.Assert;
|
|||||||
* @author Vedran Pavic
|
* @author Vedran Pavic
|
||||||
* @author Glenn Renfro
|
* @author Glenn Renfro
|
||||||
* @author Kiel Boatman
|
* @author Kiel Boatman
|
||||||
|
* @author Gary Russell
|
||||||
*
|
*
|
||||||
* @since 4.3.1
|
* @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 Log logger = LogFactory.getLog(LockRegistryLeaderInitiator.class);
|
||||||
|
|
||||||
private static final Context NULL_CONTEXT = () -> false;
|
|
||||||
|
|
||||||
private final Object lifecycleMonitor = new Object();
|
private final Object lifecycleMonitor = new Object();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,6 +86,20 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe
|
|||||||
*/
|
*/
|
||||||
private final Candidate candidate;
|
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.
|
* 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() {
|
public Context getContext() {
|
||||||
if (this.leaderSelector == null) {
|
if (this.leaderSelector == null) {
|
||||||
return NULL_CONTEXT;
|
return this.nullContext;
|
||||||
}
|
}
|
||||||
return this.leaderSelector.context;
|
return this.leaderSelector.context;
|
||||||
}
|
}
|
||||||
@@ -520,6 +533,11 @@ public class LockRegistryLeaderInitiator implements SmartLifecycle, DisposableBe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRole() {
|
||||||
|
return LockRegistryLeaderInitiator.this.candidate.getRole();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "LockContext{role=" + LockRegistryLeaderInitiator.this.candidate.getRole() +
|
return "LockContext{role=" + LockRegistryLeaderInitiator.this.candidate.getRole() +
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.integration.jdbc.leader;
|
package org.springframework.integration.jdbc.leader;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
@@ -98,7 +99,9 @@ public class JdbcLockRegistryLeaderInitiatorTests {
|
|||||||
assertNotNull(initiator2);
|
assertNotNull(initiator2);
|
||||||
|
|
||||||
assertThat(initiator1.getContext().isLeader(), is(true));
|
assertThat(initiator1.getContext().isLeader(), is(true));
|
||||||
|
assertThat(initiator1.getContext().getRole(), equalTo("bar"));
|
||||||
assertThat(initiator2.getContext().isLeader(), is(false));
|
assertThat(initiator2.getContext().isLeader(), is(false));
|
||||||
|
assertThat(initiator2.getContext().getRole(), equalTo("bar"));
|
||||||
|
|
||||||
final CountDownLatch granted1 = new CountDownLatch(1);
|
final CountDownLatch granted1 = new CountDownLatch(1);
|
||||||
final CountDownLatch granted2 = new CountDownLatch(1);
|
final CountDownLatch granted2 = new CountDownLatch(1);
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ public class LeaderInitiator implements SmartLifecycle {
|
|||||||
|
|
||||||
private static final String DEFAULT_NAMESPACE = "/spring-integration/leader/";
|
private static final String DEFAULT_NAMESPACE = "/spring-integration/leader/";
|
||||||
|
|
||||||
private static final Context NULL_CONTEXT = () -> false;
|
|
||||||
|
|
||||||
private final CuratorContext context = new CuratorContext();
|
private final CuratorContext context = new CuratorContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,6 +59,20 @@ public class LeaderInitiator implements SmartLifecycle {
|
|||||||
*/
|
*/
|
||||||
private final Candidate candidate;
|
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();
|
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.
|
* The context of the initiator.
|
||||||
* @return the context (or null if not running)
|
* @return the context.
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
if (this.leaderSelector == null) {
|
if (this.leaderSelector == null) {
|
||||||
return NULL_CONTEXT;
|
return nullContext;
|
||||||
}
|
}
|
||||||
return this.context;
|
return this.context;
|
||||||
}
|
}
|
||||||
@@ -292,6 +304,11 @@ public class LeaderInitiator implements SmartLifecycle {
|
|||||||
LeaderInitiator.this.leaderSelector.interruptLeadership();
|
LeaderInitiator.this.leaderSelector.interruptLeadership();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRole() {
|
||||||
|
return LeaderInitiator.this.candidate.getRole();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "CuratorContext{role=" + LeaderInitiator.this.candidate.getRole() +
|
return "CuratorContext{role=" + LeaderInitiator.this.candidate.getRole() +
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.integration.zookeeper.config;
|
package org.springframework.integration.zookeeper.config;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -91,7 +92,7 @@ public class LeaderInitiatorFactoryBeanTests extends ZookeeperTestSupport {
|
|||||||
public void testExceptionFromEvent() throws Exception {
|
public void testExceptionFromEvent() throws Exception {
|
||||||
CountDownLatch onGranted = new CountDownLatch(1);
|
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() {
|
initiator.setLeaderEventPublisher(new DefaultLeaderEventPublisher() {
|
||||||
|
|
||||||
@@ -107,10 +108,12 @@ public class LeaderInitiatorFactoryBeanTests extends ZookeeperTestSupport {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
assertThat(initiator.getContext().getRole(), equalTo("bar"));
|
||||||
initiator.start();
|
initiator.start();
|
||||||
|
|
||||||
assertTrue(onGranted.await(10, TimeUnit.SECONDS));
|
assertTrue(onGranted.await(10, TimeUnit.SECONDS));
|
||||||
assertTrue(initiator.getContext().isLeader());
|
assertTrue(initiator.getContext().isLeader());
|
||||||
|
assertThat(initiator.getContext().getRole(), equalTo("bar"));
|
||||||
|
|
||||||
initiator.stop();
|
initiator.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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".
|
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.
|
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.
|
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.
|
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`.
|
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.
|
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:
|
To use it you just need to create an instance as a bean, for example:
|
||||||
|
|||||||
Reference in New Issue
Block a user