INT-3617: ZK Leader Event Processing

JIRA: https://jira.spring.io/browse/INT-3617

Start/stop `SmartLifecyle` beans with leader election/revocation.

ZK Namespace Support

Add Annotation Support

INT-3617: Polishing

Add `SmartLifecycle` to listener parser.

INT-3617: Polishing; PR Comments

Revert SmartLifecycleRoleController

Remove reflection.

Remove Dependence on spring-cloud-cluster

Temporarily move the relevant classes here.

Polishing; PR Comments and Fix Test

Test was incorrectly stopping the LeaderInitiator before it was elected.
Set auto-startup="false" in the i-c-a so we wait for its start before
stopping the LeaderInitiator.

Fix JavaDocs
This commit is contained in:
Gary Russell
2015-06-18 16:33:35 -04:00
committed by Artem Bilan
parent 9cc0652b61
commit 814698fbb9
42 changed files with 1829 additions and 12 deletions

View File

@@ -72,7 +72,7 @@ public class ZookeeperTestSupport {
CloseableUtils.closeQuietly(this.client);
}
protected CuratorFramework createNewClient() throws InterruptedException {
protected static CuratorFramework createNewClient() throws InterruptedException {
CuratorFramework client = CuratorFrameworkFactory.newClient(testingServer.getConnectString(),
new BoundedExponentialBackoffRetry(100, 1000, 3));
client.start();

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-zk="http://www.springframework.org/schema/integration/zookeeper"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/zookeeper http://www.springframework.org/schema/integration/zookeeper/spring-integration-zookeeper.xsd">
<bean class="org.springframework.integration.zookeeper.config.xml.ZookeeperParserTests$Config" />
<int-zk:leader-listener client="client" path="/siNamespaceTest" role="cluster" auto-startup="false"
phase="1234" />
<int:inbound-channel-adapter id="ica" channel="nullChannel" expression="'foo'" role="cluster"
auto-startup="false">
<int:poller fixed-rate="60000" />
</int:inbound-channel-adapter>
</beans>

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2015 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.zookeeper.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import org.apache.curator.framework.CuratorFramework;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.zookeeper.ZookeeperTestSupport;
import org.springframework.integration.zookeeper.leader.LeaderInitiator;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 4.2
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class ZookeeperParserTests extends ZookeeperTestSupport {
@Autowired
private LeaderInitiator initiator;
@Autowired
private SourcePollingChannelAdapter adapter;
@Autowired
private CuratorFramework client;
@Test
public void test() throws Exception {
assertFalse(this.initiator.isAutoStartup());
assertEquals(1234, this.initiator.getPhase());
assertEquals("/siNamespaceTest", TestUtils.getPropertyValue(this.initiator, "namespace"));
assertEquals("cluster", TestUtils.getPropertyValue(this.initiator, "candidate.role"));
assertSame(this.client, TestUtils.getPropertyValue(this.initiator, "client"));
this.initiator.start();
int n = 0;
while (n++ < 100 && !this.adapter.isRunning()) {
Thread.sleep(100);
}
assertTrue(this.adapter.isRunning());
this.initiator.stop();
n = 0;
while (n++ < 100 && this.adapter.isRunning()) {
Thread.sleep(100);
}
assertFalse(this.adapter.isRunning());
}
public static class Config {
@Bean
public CuratorFramework client() throws Exception {
return createNewClient();
}
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright 2015 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.zookeeper.event;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import java.util.Collections;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.SmartLifecycle;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.leader.DefaultCandidate;
import org.springframework.integration.leader.event.AbstractLeaderEvent;
import org.springframework.integration.leader.event.DefaultLeaderEventPublisher;
import org.springframework.integration.leader.event.LeaderEventPublisher;
import org.springframework.integration.leader.event.OnGrantedEvent;
import org.springframework.integration.leader.event.OnRevokedEvent;
import org.springframework.integration.support.SmartLifecycleRoleController;
import org.springframework.integration.zookeeper.ZookeeperTestSupport;
import org.springframework.integration.zookeeper.leader.LeaderInitiator;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.PeriodicTrigger;
/**
* @author Gary Russell
* @since 4.2
*
*/
public class ZookeeperLeaderTests extends ZookeeperTestSupport {
private final BlockingQueue<AbstractLeaderEvent> events = new LinkedBlockingQueue<AbstractLeaderEvent>();
private final SourcePollingChannelAdapter adapter = buildChannelAdapter();
private final SmartLifecycleRoleController controller = new SmartLifecycleRoleController(
Collections.singletonList("sitest"), Collections.<SmartLifecycle>singletonList(this.adapter));
@Test
public void testLeader() throws Exception {
LeaderEventPublisher publisher = publisher();
DefaultCandidate candidate1 = new DefaultCandidate("foo", "sitest");
LeaderInitiator initiator1 = new LeaderInitiator(this.client, candidate1, "/sitest");
initiator1.setLeaderEventPublisher(publisher);
initiator1.start();
DefaultCandidate candidate2 = new DefaultCandidate("bar", "sitest");
LeaderInitiator initiator2 = new LeaderInitiator(this.client, candidate2, "/sitest");
initiator2.setLeaderEventPublisher(publisher);
initiator2.start();
AbstractLeaderEvent event = this.events.poll(10, TimeUnit.SECONDS);
assertNotNull(event);
assertThat(event, instanceOf(OnGrantedEvent.class));
event.getContext().yield();
assertTrue(this.adapter.isRunning());
event = this.events.poll(10, TimeUnit.SECONDS);
assertNotNull(event);
assertThat(event, instanceOf(OnRevokedEvent.class));
assertFalse(this.adapter.isRunning());
event = this.events.poll(10, TimeUnit.SECONDS);
assertNotNull(event);
assertThat(event, instanceOf(OnGrantedEvent.class));
assertTrue(this.adapter.isRunning());
initiator1.stop();
initiator2.stop();
event = this.events.poll(10, TimeUnit.SECONDS);
assertNotNull(event);
assertThat(event, instanceOf(OnRevokedEvent.class));
assertFalse(this.adapter.isRunning());
}
private LeaderEventPublisher publisher() {
return new DefaultLeaderEventPublisher(new ApplicationEventPublisher() {
@Override
public void publishEvent(Object event) {
}
@Override
public void publishEvent(ApplicationEvent event) {
AbstractLeaderEvent leadershipEvent = (AbstractLeaderEvent) event;
controller.onApplicationEvent((AbstractLeaderEvent) event);
events.add(leadershipEvent);
}
});
}
private SourcePollingChannelAdapter buildChannelAdapter() {
SourcePollingChannelAdapter adapter = new SourcePollingChannelAdapter();
adapter.setSource(new MessageSource<String>() {
@Override
public Message<String> receive() {
return new GenericMessage<String>("foo");
}
});
adapter.setOutputChannel(new QueueChannel());
adapter.setTrigger(new PeriodicTrigger(10000));
adapter.setBeanFactory(mock(BeanFactory.class));
ThreadPoolTaskScheduler sched = new ThreadPoolTaskScheduler();
sched.afterPropertiesSet();
adapter.setTaskScheduler(sched);
adapter.afterPropertiesSet();
return adapter;
}
}