INT-4236: Status for SmartLifecycleRoleControlller

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

Add methods to obtain status from the `SmartLifecycleRoleController`.

* Polishing according PR comments.
* Fix log messages in the `SmartLifecycleRoleController` from the Zookeeper mentioning
This commit is contained in:
Gary Russell
2017-02-23 13:24:23 -05:00
committed by Artem Bilan
parent 257a2d3dab
commit f23360d539
4 changed files with 115 additions and 7 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 2015-2017 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.
@@ -17,10 +17,14 @@
package org.springframework.integration.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -30,10 +34,12 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.integration.leader.event.AbstractLeaderEvent;
import org.springframework.integration.leader.event.OnGrantedEvent;
import org.springframework.integration.leader.event.OnRevokedEvent;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -42,6 +48,8 @@ import org.springframework.util.MultiValueMap;
* Bulk start/stop {@link SmartLifecycle} in a particular role in phase order.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 4.2
*
*/
@@ -135,7 +143,7 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
Collections.sort(lifecycles, (o1, o2) ->
o1.getPhase() < o2.getPhase() ? -1 : o1.getPhase() > o2.getPhase() ? 1 : 0);
if (logger.isDebugEnabled()) {
logger.debug("Zookeeper leadership granted: Starting: " + lifecycles);
logger.debug("Starting " + lifecycles + " in role " + role);
}
for (SmartLifecycle lifecycle : lifecycles) {
try {
@@ -148,7 +156,7 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Zookeeper leadership granted: Nothing to do");
logger.debug("No components in role " + role + ". Nothing to start");
}
}
}
@@ -167,7 +175,7 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
Collections.sort(lifecycles, (o1, o2) ->
o1.getPhase() < o2.getPhase() ? 1 : o1.getPhase() > o2.getPhase() ? -1 : 0);
if (logger.isDebugEnabled()) {
logger.debug("Zookeeper leadership revoked: Stopping: " + lifecycles);
logger.debug("Stopping " + lifecycles + " in role " + role);
}
for (SmartLifecycle lifecycle : lifecycles) {
try {
@@ -180,12 +188,71 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Zookeeper leadership revoked: Nothing to do");
logger.debug("No components in role " + role + ". Nothing to stop");
}
}
}
private void addLazyLifecycles() {
/**
* Return a collection of the roles currently managed by this controller.
* @return the roles.
* @since 4.3.8
*/
public Collection<String> getRoles() {
if (this.lazyLifecycles.size() > 0) {
addLazyLifecycles();
}
return new ArrayList<>(this.lifecycles.keySet());
}
/**
* Return true if all endpoints in the role are running.
* @param role the role.
* @return true if at least one endpoint in the role, and all are running.
* @since 4.3.8
*/
public boolean allEndpointsRunning(String role) {
Map<String, Boolean> status = getEndpointsRunningStatus(role);
return !status.isEmpty() && status.values().stream().allMatch(b -> b);
}
/**
* Return true if none of the endpoints in the role are running or if
* there are no endpoints in the role.
* @param role the role.
* @return true if there are no endpoints or none are running.
* @since 4.3.8
*/
public boolean noEndpointsRunning(String role) {
Map<String, Boolean> status = getEndpointsRunningStatus(role);
return status.isEmpty() || status.values().stream().noneMatch(b -> b);
}
/**
* Return the running status of each endpoint in the role.
* @param role the role.
* @return A map of component names : running status
* @since 4.3.8
*/
public Map<String, Boolean> getEndpointsRunningStatus(String role) {
if (this.lazyLifecycles.size() > 0) {
addLazyLifecycles();
}
if (!this.lifecycles.containsKey(role)) {
return Collections.emptyMap();
}
AtomicInteger index = new AtomicInteger();
return this.lifecycles.get(role)
.stream()
.collect(Collectors.toMap(e ->
(e instanceof NamedComponent)
? ((NamedComponent) e).getComponentName()
: (e.getClass().getSimpleName() + "#" + index.getAndIncrement()),
Lifecycle::isRunning));
}
private synchronized void addLazyLifecycles() {
for (Entry<String, List<String>> entry : this.lazyLifecycles.entrySet()) {
doAddLifecyclesToRole(entry.getKey(), entry.getValue());
}
@@ -199,7 +266,7 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
addLifecycleToRole(role, lifecycle);
}
catch (NoSuchBeanDefinitionException e) {
logger.warn("Skipped; no such bean :" + lifecycleBeanName);
logger.warn("Skipped; no such bean: " + lifecycleBeanName);
}
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.integration.configuration;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -36,6 +38,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -630,7 +633,21 @@ public class EnableIntegrationTests {
@Test
@DirtiesContext
public void testRoles() {
assertThat(this.roleController.getRoles(), containsInAnyOrder("foo", "bar"));
assertFalse(this.roleController.allEndpointsRunning("foo"));
assertFalse(this.roleController.noEndpointsRunning("foo"));
assertTrue(this.roleController.allEndpointsRunning("bar"));
assertFalse(this.roleController.noEndpointsRunning("bar"));
Map<String, Boolean> state = this.roleController.getEndpointsRunningStatus("foo");
assertThat(state.get("annotationTestService.handle.serviceActivator"), equalTo(Boolean.FALSE));
assertThat(state.get("enableIntegrationTests.ContextConfiguration2.sendAsyncHandler.serviceActivator"),
equalTo(Boolean.TRUE));
this.roleController.startLifecyclesInRole("foo");
assertTrue(this.roleController.allEndpointsRunning("foo"));
this.roleController.stopLifecyclesInRole("foo");
assertFalse(this.roleController.allEndpointsRunning("foo"));
assertTrue(this.roleController.noEndpointsRunning("foo"));
@SuppressWarnings("unchecked")
MultiValueMap<String, SmartLifecycle> lifecycles = TestUtils.getPropertyValue(this.roleController,
"lifecycles", MultiValueMap.class);

View File

@@ -633,6 +633,27 @@ start/stop its configured `SmartLifecycle` objects when leadership is granted/re
IMPORTANT: When using leadership election to start/stop components, it is important to set the `auto-startup` XML attribute (`autoStartup` bean property) to `false` so the application context does not start the components during context intialization.
Starting with _version 4.3.8, the `SmartLifecycleRoleController` provides several status methods:
[source, java]
----
public Collection<String> getRoles() <1>
public boolean allEndpointsRunning(String role) <2>
public boolean noEndpointsRunning(String role) <3>
public Map<String, Boolean> getEndpointsRunningStatus(String role) <4>
----
<1> Returns a list of the roles being managed.
<2> Returns true if all endpoints in the role are running.
<3> Returns true if none of the endpoints in the role are running.
<4> Returns a map of `component name : running status` - the component name is usually the bean name.
[[leadership-event-handling]]
=== Leadership Event Handling

View File

@@ -33,6 +33,9 @@ See <<annotations>> for more information.
All the request-reply endpoints (based on `AbstractReplyProducingMessageHandler`) can now start transaction and, therefore, make the whole downstream flow transactional.
See <<tx-handle-message-advice>> for more information.
The `SmartLifecycleRoleController` now provides methods to obtain status of endpoints in roles.
See <<endpoint-roles>> for more information.
==== JMS Changes
Previously, Spring Integration JMS XML configuration used a default bean name `connectionFactory` for the JMS Connection Factory, allowing the property to be omitted from component definitions.