/*
* Copyright 2002-2011 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.cluster;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.integration.cluster.ClusterControlImpl;
import org.springframework.integration.cluster.ClusterStatus;
import org.springframework.integration.cluster.ClusterStatusRepository;
import org.springframework.integration.cluster.ControlBusGateway;
import org.springframework.integration.cluster.Heartbeat;
import org.springframework.integration.cluster.HeartbeatGateway;
/**
* @author Gary Russell
*
*/
public class ClusterControlImplTests {
@Mock
private ControlBusGateway controlBusGateway;
@Mock
private HeartbeatGateway<Heartbeat> heartbeatGateway;
@Mock
private ClusterStatusRepository clusterStatusRepository;
private ClusterStatus clusterStatus = new ClusterStatus("foo", "bar");
private ClusterStatus clusterStatusFromStub;
int interval = 1000;
int delay = (int) (interval * 2.1);
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testVerifyStatusMaster() {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
60000, this.controlBusGateway, this.heartbeatGateway, this.clusterStatusRepository);
when(clusterStatusRepository.lock("foo")).thenReturn(clusterStatus);
clusterStatus.setPendingUsurper("baz");
assertTrue(clusterControl.verifyStatus(false));
verify(clusterStatusRepository).updateLastProcessed(clusterStatus);
assertEquals("", clusterStatus.getPendingUsurper());
// second call to take short path
assertTrue(clusterControl.verifyStatus(false));
}
@Test
public void testVerifyStatusNotMaster() {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway, this.clusterStatusRepository);
clusterStatus.setCurrentMaster("baz");
when(clusterStatusRepository.lock("foo")).thenReturn(clusterStatus);
assertFalse(clusterControl.verifyStatus(false));
verify(clusterStatusRepository, never()).updateLastProcessed(clusterStatus);
verify(this.controlBusGateway).sendCommand("@'inboundAdapter'.stop()");
}
@Test
public void testHeartbeat() {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway, this.clusterStatusRepository);
clusterControl.sendHeartbeat();
verify(this.heartbeatGateway).sendHeartbeat(any(Heartbeat.class));
}
@Test
public void testStop() {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway, this.clusterStatusRepository);
clusterControl.stopInbound();
verify(this.controlBusGateway).sendCommand("@'inboundAdapter'.stop()");
}
@Test
public void testStart() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway, this.clusterStatusRepository);
when(clusterStatusRepository.lock("foo")).thenReturn(clusterStatus);
clusterControl.start();
clusterControl.doMonitor();
clusterControl.startInbound();
verify(this.controlBusGateway,times(2)).sendCommand("@'inboundAdapter'.start()");
}
@Test
public void testStartDisallowed() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway, this.clusterStatusRepository);
clusterStatus.setCurrentMaster("baz");
when(clusterStatusRepository.find("foo")).thenReturn(clusterStatus);
when(clusterStatusRepository.lock("foo")).thenReturn(clusterStatus);
clusterControl.doMonitor();
try {
clusterControl.startInbound();
fail("Expected Exception");
} catch (RuntimeException e) {
assertTrue(e.getMessage().startsWith("Cannot start adapter"));
}
}
@Test
public void testMaster() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway,
new ClusterStatusRepositoryStub());
clusterControl.start();
clusterControl.doMonitor(); // create initial status object
verify(controlBusGateway).sendCommand("@'inboundAdapter'.start()");
assertNotNull(clusterStatusFromStub);
assertEquals("foo", clusterStatusFromStub.getApplicationId());
assertEquals("bar", clusterStatusFromStub.getCurrentMaster());
clusterControl.doMonitor();
Thread.sleep(delay);
clusterControl.doMonitor();
verify(heartbeatGateway).sendHeartbeat(any(Heartbeat.class));
clusterControl.doMonitor();
Thread.sleep(delay*2);
clusterControl.doMonitor();
verify(controlBusGateway).sendCommand("@'inboundAdapter'.stop()");
}
@Test
public void testNoMaster() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway,
this.clusterStatusRepository);
clusterControl.start();
ClusterStatus clusterStatus = new ClusterStatus("foo", "");
when(clusterStatusRepository.lock("foo")).thenReturn(clusterStatus);
clusterControl.doMonitor(); // create initial status object
verify(controlBusGateway).sendCommand("@'inboundAdapter'.start()");
assertNotNull(clusterStatus.getLastProcessed());
assertEquals("foo", clusterStatus.getApplicationId());
assertEquals("bar", clusterStatus.getCurrentMaster());
}
@Test
public void testNoRow() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway,
this.clusterStatusRepository);
clusterControl.start();
ClusterStatus clusterStatus = new ClusterStatus("foo", "bar");
when(clusterStatusRepository.lock("foo"))
.thenThrow(new EmptyResultDataAccessException(1))
.thenReturn(clusterStatus);
clusterControl.doMonitor(); // create initial status object
verify(clusterStatusRepository).create(any(ClusterStatus.class));
verify(controlBusGateway).sendCommand("@'inboundAdapter'.start()");
assertNotNull(clusterStatus.getLastProcessed());
assertEquals("foo", clusterStatus.getApplicationId());
assertEquals("bar", clusterStatus.getCurrentMaster());
}
@Test
public void testNoRowLoser() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway,
this.clusterStatusRepository);
clusterControl.start();
ClusterStatus clusterStatus = new ClusterStatus("foo", "baz");
when(clusterStatusRepository.lock("foo"))
.thenThrow(new EmptyResultDataAccessException(1))
.thenReturn(clusterStatus);
doThrow(new DuplicateKeyException("test")).when(clusterStatusRepository).create(any(ClusterStatus.class));
clusterControl.doMonitor(); // create initial status object
verify(clusterStatusRepository).create(any(ClusterStatus.class));
verify(controlBusGateway).sendCommand("@'inboundAdapter'.stop()");
}
@Test
public void testUsurper() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", delay,
interval * 2, this.controlBusGateway, this.heartbeatGateway,
new ClusterStatusRepositoryStub());
clusterControl.start();
clusterControl.doMonitor();
verify(controlBusGateway).sendCommand("@'inboundAdapter'.start()");
assertNotNull(clusterStatusFromStub);
assertEquals("foo", clusterStatusFromStub.getApplicationId());
assertEquals("bar", clusterStatusFromStub.getCurrentMaster());
clusterStatusFromStub.setCurrentMaster("baz");
clusterControl.doMonitor();
Thread.sleep(delay*4);
clusterControl.doMonitor();
Thread.sleep(delay*4);
clusterControl.doMonitor();
verify(controlBusGateway,times(2)).sendCommand("@'inboundAdapter'.start()");
}
@Test
public void testOtherUsurper() throws Exception {
ClusterControlImpl clusterControl = new ClusterControlImpl("foo", true, "bar",
"inboundAdapter", 30000,
interval * 2, this.controlBusGateway, this.heartbeatGateway,
new ClusterStatusRepositoryStub());
clusterControl.start();
clusterControl.doMonitor();
verify(controlBusGateway).sendCommand("@'inboundAdapter'.start()");
assertNotNull(clusterStatusFromStub);
assertEquals("foo", clusterStatusFromStub.getApplicationId());
assertEquals("bar", clusterStatusFromStub.getCurrentMaster());
clusterStatusFromStub.setCurrentMaster("baz");
clusterControl.doMonitor();
Thread.sleep(delay);
clusterControl.doMonitor();
clusterStatusFromStub.setPendingUsurper("quz");
Thread.sleep(delay/2);
clusterControl.doMonitor();
assertEquals("quz", clusterStatusFromStub.getPendingUsurper());
}
private class ClusterStatusRepositoryStub implements ClusterStatusRepository {
public void create(ClusterStatus clusterStatus) {
clusterStatusFromStub = clusterStatus;
}
public ClusterStatus find(String applicationId) {
return clusterStatusFromStub;
}
public ClusterStatus lock(String applicationId) {
return clusterStatusFromStub;
}
public void updateLastProcessed(ClusterStatus clusterStatus) {
assertSame(clusterStatus, clusterStatusFromStub);
}
public void updateUsurper(ClusterStatus clusterStatus) {
assertSame(clusterStatus, clusterStatusFromStub);
}
public void updateMaster(ClusterStatus clusterStatus) {
assertSame(clusterStatus, clusterStatusFromStub);
}
public int updateStatusAll(String applicationId, String status) {
return 0;
}
public void unlock(String applicationId) {
}
}
}