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:
committed by
Artem Bilan
parent
9cc0652b61
commit
814698fbb9
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotate endpoints to assign them to a role. Such endpoints can be started/stopped as
|
||||
* a group. See {@code SmartLifecycleRoleController}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 4.2
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Role {
|
||||
|
||||
/**
|
||||
* @return the role for this endpoint. See {@code SmartLifecycleRoleController}.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -20,7 +20,11 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.support.SmartLifecycleRoleController;
|
||||
|
||||
/**
|
||||
* Shared utility methods for Integration configuration.
|
||||
@@ -51,6 +55,17 @@ public final class IntegrationConfigUtils {
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
|
||||
}
|
||||
|
||||
public static void registerRoleControllerDefinitionIfNecessary(BeanDefinitionRegistry registry) {
|
||||
if (!registry.containsBeanDefinition(
|
||||
IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER)) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SmartLifecycleRoleController.class);
|
||||
builder.addConstructorArgValue(new ManagedList<String>());
|
||||
builder.addConstructorArgValue(new ManagedList<Lifecycle>());
|
||||
registry.registerBeanDefinition(
|
||||
IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER, builder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private IntegrationConfigUtils() {
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -49,9 +52,6 @@ import org.springframework.integration.support.converter.DefaultDatatypeChannelM
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* {@link ImportBeanDefinitionRegistrar} implementation that configures integration infrastructure.
|
||||
*
|
||||
@@ -102,6 +102,7 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
|
||||
this.registerMessagingAnnotationPostProcessors(importingClassMetadata, registry);
|
||||
}
|
||||
this.registerMessageBuilderFactory(registry);
|
||||
IntegrationConfigUtils.registerRoleControllerDefinitionIfNecessary(registry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -35,6 +36,8 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
@@ -49,15 +52,20 @@ import org.springframework.integration.annotation.BridgeFrom;
|
||||
import org.springframework.integration.annotation.BridgeTo;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.annotation.InboundChannelAdapter;
|
||||
import org.springframework.integration.annotation.Role;
|
||||
import org.springframework.integration.annotation.Router;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.annotation.Splitter;
|
||||
import org.springframework.integration.annotation.Transformer;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.support.SmartLifecycleRoleController;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -71,7 +79,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class MessagingAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware,
|
||||
InitializingBean, Lifecycle, ApplicationListener<ApplicationEvent>, EnvironmentAware {
|
||||
InitializingBean, Lifecycle, ApplicationListener<ApplicationEvent>, EnvironmentAware,
|
||||
SmartInitializingSingleton {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
@@ -88,6 +97,7 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
|
||||
private volatile boolean running = true;
|
||||
|
||||
private final MultiValueMap<String, String> lazyLifecyleRoles = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
@@ -120,6 +130,21 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
SmartLifecycleRoleController roleController;
|
||||
try {
|
||||
roleController = beanFactory.getBean(IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER,
|
||||
SmartLifecycleRoleController.class);
|
||||
for (Entry<String, List<String>> entry : this.lazyLifecyleRoles.entrySet()) {
|
||||
roleController.addLifecyclesToRole(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
logger.error("No lifecyle role controller in context");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
|
||||
Assert.notNull(this.beanFactory, "BeanFactory must not be null");
|
||||
@@ -200,6 +225,10 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
if (result instanceof ApplicationListener) {
|
||||
listeners.add((ApplicationListener) result);
|
||||
}
|
||||
Role role = AnnotationUtils.findAnnotation(method, Role.class);
|
||||
if (role != null) {
|
||||
lazyLifecyleRoles.add(role.value(), endpointBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,13 @@ public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitio
|
||||
if (StringUtils.hasText(phase)) {
|
||||
propertyValues.add("phase", new TypedStringValue(phase));
|
||||
}
|
||||
String role = element.getAttribute(IntegrationNamespaceUtils.ROLE);
|
||||
if (StringUtils.hasText(role)) {
|
||||
if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) {
|
||||
parserContext.getReaderContext().error("When using 'role', 'id' is required", element);
|
||||
}
|
||||
IntegrationNamespaceUtils.putLifecycleInRole(role, element.getAttribute(ID_ATTRIBUTE), parserContext);
|
||||
}
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,13 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.AUTO_STARTUP);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.PHASE);
|
||||
String role = element.getAttribute(IntegrationNamespaceUtils.ROLE);
|
||||
if (StringUtils.hasText(role)) {
|
||||
if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) {
|
||||
parserContext.getReaderContext().error("When using 'role', 'id' is required", element);
|
||||
}
|
||||
IntegrationNamespaceUtils.putLifecycleInRole(role, element.getAttribute(ID_ATTRIBUTE), parserContext);
|
||||
}
|
||||
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
|
||||
String beanName = this.resolveId(element, beanDefinition, parserContext);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, beanName));
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -72,6 +73,7 @@ public abstract class IntegrationNamespaceUtils {
|
||||
public static final String REQUEST_HANDLER_ADVICE_CHAIN = "request-handler-advice-chain";
|
||||
public static final String AUTO_STARTUP = "auto-startup";
|
||||
public static final String PHASE = "phase";
|
||||
public static final String ROLE = "role";
|
||||
|
||||
/**
|
||||
* Configures the provided bean definition builder with a property value corresponding to the attribute whose name
|
||||
@@ -547,4 +549,19 @@ public abstract class IntegrationNamespaceUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void putLifecycleInRole(String role, String beanName, ParserContext parserContext) {
|
||||
BeanDefinitionRegistry registry = parserContext.getRegistry();
|
||||
IntegrationConfigUtils.registerRoleControllerDefinitionIfNecessary(registry);
|
||||
BeanDefinition controllerDef = registry.getBeanDefinition(
|
||||
IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER);
|
||||
@SuppressWarnings("unchecked")
|
||||
ManagedList<String> roles = (ManagedList<String>) controllerDef.getConstructorArgumentValues()
|
||||
.getArgumentValue(0, ManagedList.class).getValue();
|
||||
@SuppressWarnings("unchecked")
|
||||
ManagedList<BeanReference> lifecycles = (ManagedList<BeanReference>) controllerDef.getConstructorArgumentValues()
|
||||
.getArgumentValue(1, ManagedList.class).getValue();
|
||||
roles.add(role);
|
||||
lifecycles.add(new RuntimeBeanReference(beanName));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +77,8 @@ public abstract class IntegrationContextUtils {
|
||||
public static final String TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME =
|
||||
"toStringFriendlyJsonNodeToStringConverter";
|
||||
|
||||
public static final String INTEGRATION_LIFECYCLE_ROLE_CONTROLLER = "integrationLifecycleRoleController";
|
||||
|
||||
/**
|
||||
* @param beanFactory BeanFactory for lookup, must not be null.
|
||||
* @return The {@link MetadataStore} bean whose name is "metadataStore".
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.leader;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base implementation of a {@link Candidate}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractCandidate implements Candidate {
|
||||
|
||||
private static final String DEFAULT_ROLE = "leader";
|
||||
|
||||
private final String id;
|
||||
|
||||
private final String role;
|
||||
|
||||
/**
|
||||
* Instantiate a abstract candidate.
|
||||
*/
|
||||
public AbstractCandidate() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a abstract candidate.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @param role the role
|
||||
*/
|
||||
public AbstractCandidate(String id, String role) {
|
||||
this.id = StringUtils.hasText(id) ? id : UUID.randomUUID().toString();
|
||||
this.role = StringUtils.hasText(role) ? role : DEFAULT_ROLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void onGranted(Context ctx) throws InterruptedException;
|
||||
|
||||
@Override
|
||||
public abstract void onRevoked(Context ctx);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2014-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.leader;
|
||||
|
||||
/**
|
||||
* Interface that defines the contract for candidates to participate
|
||||
* in a leader election. The callback methods {@link #onGranted(Context)}
|
||||
* and {@link #onRevoked(Context)} are invoked when leadership is
|
||||
* granted and revoked.
|
||||
*
|
||||
* @author Patrick Peralta
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface Candidate {
|
||||
|
||||
/**
|
||||
* Gets the role.
|
||||
*
|
||||
* @return a string indicating the name of the leadership role
|
||||
* this candidate is participating in; other candidates
|
||||
* present in the system with the same name will contend
|
||||
* for leadership
|
||||
*/
|
||||
String getRole();
|
||||
|
||||
/**
|
||||
* Gets the identifier.
|
||||
*
|
||||
* @return a unique ID for this candidate; no other candidate for
|
||||
* leader election should return the same id
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Callback method invoked when this candidate is elected leader.
|
||||
* Implementations may chose to launch a background thread to
|
||||
* perform leadership roles and return immediately. Another option
|
||||
* is for implementations to perform all leadership work in the
|
||||
* thread invoking this method. In the latter case, the
|
||||
* method <em>must</em> respond to thread interrupts by throwing
|
||||
* {@link java.lang.InterruptedException}. When the thread
|
||||
* is interrupted, this indicates that this candidate is no
|
||||
* longer leader.
|
||||
*
|
||||
* @param ctx leadership context
|
||||
* @throws InterruptedException when this candidate is no longer leader
|
||||
*/
|
||||
void onGranted(Context ctx) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Callback method invoked when this candidate is no longer leader.
|
||||
* Implementations should use this to shut down any resources
|
||||
* (threads, network connections, etc) used to perform leadership work.
|
||||
*
|
||||
* @param ctx leadership context
|
||||
*/
|
||||
void onRevoked(Context ctx);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014-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.leader;
|
||||
|
||||
/**
|
||||
* Interface that defines the context for candidate leadership.
|
||||
* Instances of this object are passed to {@link Candidate candidates}
|
||||
* upon granting and revoking of leadership.
|
||||
*
|
||||
* @author Patrick Peralta
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface Context {
|
||||
|
||||
/**
|
||||
* Checks if the {@link Candidate} this context was
|
||||
* passed to is the leader.
|
||||
*
|
||||
* @return true if the {@link Candidate} this context was
|
||||
* passed to is the leader
|
||||
*/
|
||||
boolean isLeader();
|
||||
|
||||
/**
|
||||
* Causes the {@link Candidate} this context was passed to
|
||||
* to relinquish leadership. This method has no effect
|
||||
* if the candidate is not currently the leader.
|
||||
*/
|
||||
void yield();
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2014-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.leader;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple {@link Candidate} for leadership.
|
||||
* This implementation simply logs when it is elected and when its leadership is revoked.
|
||||
*/
|
||||
public class DefaultCandidate extends AbstractCandidate {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private volatile Context leaderContext;
|
||||
|
||||
/**
|
||||
* Instantiate a default candidate.
|
||||
*/
|
||||
public DefaultCandidate() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a default candidate.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @param role the role
|
||||
*/
|
||||
public DefaultCandidate(String id, String role) {
|
||||
super(id, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGranted(Context ctx) {
|
||||
logger.info("{} has been granted leadership; context: {}", this, ctx);
|
||||
leaderContext = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevoked(Context ctx) {
|
||||
logger.info("{} leadership has been revoked", this, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Voluntarily yield leadership if held. If leader context is not
|
||||
* yet known this method does nothing. Leader context becomes available
|
||||
* only after {@link #onGranted(Context)} method is called by the
|
||||
* leader initiator.
|
||||
*/
|
||||
public void yieldLeadership() {
|
||||
if (leaderContext != null) {
|
||||
leaderContext.yield();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DefaultCandidate{role=%s, id=%s}", getRole(), getId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.leader.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.integration.leader.Context;
|
||||
|
||||
/**
|
||||
* Base {@link ApplicationEvent} class for leader based events. All custom event
|
||||
* classes should be derived from this class.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractLeaderEvent extends ApplicationEvent {
|
||||
|
||||
private final Context context;
|
||||
|
||||
private final String role;
|
||||
|
||||
/**
|
||||
* Create a new ApplicationEvent.
|
||||
*
|
||||
* @param source the component that published the event (never {@code null})
|
||||
*/
|
||||
public AbstractLeaderEvent(Object source) {
|
||||
this(source, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ApplicationEvent.
|
||||
*
|
||||
* @param source the component that published the event (never {@code null})
|
||||
* @param context the context associated with this event
|
||||
* @param role the role of the leader
|
||||
*/
|
||||
public AbstractLeaderEvent(Object source, Context context, String role) {
|
||||
super(source);
|
||||
this.context = context;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Context} associated with this event.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role of the leader.
|
||||
*
|
||||
* @return the role
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " [role=" + role + ", context=" + context + ", source=" + source
|
||||
+ "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.leader.event;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.integration.leader.Context;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link LeaderEventPublisher}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class DefaultLeaderEventPublisher implements LeaderEventPublisher, ApplicationEventPublisherAware {
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
/**
|
||||
* Instantiates a new leader event publisher.
|
||||
*/
|
||||
public DefaultLeaderEventPublisher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new leader event publisher.
|
||||
*
|
||||
* @param applicationEventPublisher the application event publisher
|
||||
*/
|
||||
public DefaultLeaderEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishOnGranted(Object source, Context context, String role) {
|
||||
if (applicationEventPublisher != null) {
|
||||
applicationEventPublisher.publishEvent(new OnGrantedEvent(source, context, role));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishOnRevoked(Object source, Context context, String role) {
|
||||
if (applicationEventPublisher != null) {
|
||||
applicationEventPublisher.publishEvent(new OnRevokedEvent(source, context, role));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.leader.event;
|
||||
|
||||
import org.springframework.integration.leader.Context;
|
||||
|
||||
/**
|
||||
* Interface for publishing leader based application events.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public interface LeaderEventPublisher {
|
||||
|
||||
/**
|
||||
* Publish a granted event.
|
||||
*
|
||||
* @param source the component generated this event
|
||||
* @param context the context associated with event
|
||||
* @param role the role of the leader
|
||||
*/
|
||||
void publishOnGranted(Object source, Context context, String role);
|
||||
|
||||
/**
|
||||
* Publish a revoked event.
|
||||
*
|
||||
* @param source the component generated this event
|
||||
* @param context the context associated with event
|
||||
* @param role the role of the leader
|
||||
*/
|
||||
void publishOnRevoked(Object source, Context context, String role);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.leader.event;
|
||||
|
||||
import org.springframework.integration.leader.Context;
|
||||
|
||||
/**
|
||||
* Generic event representing that leader has been granted.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OnGrantedEvent extends AbstractLeaderEvent {
|
||||
|
||||
/**
|
||||
* Instantiates a new granted event.
|
||||
*
|
||||
* @param source the component that published the event (never {@code null})
|
||||
* @param context the context associated with this event
|
||||
* @param role the role of the leader
|
||||
*/
|
||||
public OnGrantedEvent(Object source, Context context, String role) {
|
||||
super(source, context, role);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.leader.event;
|
||||
|
||||
import org.springframework.integration.leader.Context;
|
||||
|
||||
/**
|
||||
* Generic event representing that leader has been revoked.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OnRevokedEvent extends AbstractLeaderEvent {
|
||||
|
||||
/**
|
||||
* Instantiates a new revoked event.
|
||||
*
|
||||
* @param source the component that published the event (never {@code null})
|
||||
* @param context the context associated with this event
|
||||
* @param role the role of the leader
|
||||
*/
|
||||
public OnRevokedEvent(Object source, Context context, String role) {
|
||||
super(source, context, role);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Temporary package until s-c-c-core is released.
|
||||
*/
|
||||
package org.springframework.integration.leader.event;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Temporary package until s-c-c-core is released.
|
||||
*/
|
||||
package org.springframework.integration.leader;
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
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.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.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Bulk start/stop {@link SmartLifecycle} in a particular role in phase order.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 4.2
|
||||
*
|
||||
*/
|
||||
public class SmartLifecycleRoleController implements ApplicationListener<AbstractLeaderEvent>,
|
||||
ApplicationContextAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SmartLifecycleRoleController.class);
|
||||
|
||||
private final MultiValueMap<String, SmartLifecycle> lifecycles = new LinkedMultiValueMap<String, SmartLifecycle>();
|
||||
|
||||
private final MultiValueMap<String, String> lazyLifecycles = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* Construct an instance with the provided lists of roles and lifecycles, which must be of equal length.
|
||||
* @param roles the roles.
|
||||
* @param lifecycles the lifecycles corresponding to the roles.
|
||||
*/
|
||||
public SmartLifecycleRoleController(List<String> roles, List<SmartLifecycle> lifecycles) {
|
||||
Assert.notNull(roles, "'roles' cannot be null");
|
||||
Assert.notNull(lifecycles, "'lifecycles' cannot be null");
|
||||
Assert.isTrue(roles.size() == lifecycles.size(), "'roles' and 'lifecycles' must be the same lenght");
|
||||
Iterator<SmartLifecycle> iterator = lifecycles.iterator();
|
||||
for (String role : roles) {
|
||||
SmartLifecycle lifecycle = iterator.next();
|
||||
addLifecycleToRole(role, lifecycle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance with the provided map of roles/instances.
|
||||
* @param lifcycles the {@link MultiValueMap} of beans in roles.
|
||||
*/
|
||||
public SmartLifecycleRoleController(MultiValueMap<String, SmartLifecycle> lifcycles) {
|
||||
for (Entry<String, List<SmartLifecycle>> lifecyclesInRole : lifcycles.entrySet()) {
|
||||
String role = lifecyclesInRole.getKey();
|
||||
for (SmartLifecycle lifecycle : lifecyclesInRole.getValue()) {
|
||||
addLifecycleToRole(role, lifecycle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link SmartLifecycle} to the role.
|
||||
* @param role the role.
|
||||
* @param lifecycle the {@link SmartLifecycle}.
|
||||
*/
|
||||
public void addLifecycleToRole(String role, SmartLifecycle lifecycle) {
|
||||
this.lifecycles.add(role, lifecycle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link SmartLifecycle} bean to the role using its name.
|
||||
* @param role the role.
|
||||
* @param lifecycleBeanName the bean name of the {@link SmartLifecycle}.
|
||||
*/
|
||||
public void addLifecycleToRole(String role, String lifecycleBeanName) {
|
||||
Assert.state(this.applicationContext != null, "An application context is required to use this method");
|
||||
this.lazyLifecycles.add(role, lifecycleBeanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link SmartLifecycle} beans to the role using their names.
|
||||
* @param role the role.
|
||||
* @param lifecycleBeanNames the bean names of the {@link SmartLifecycle}s.
|
||||
*/
|
||||
public void addLifecyclesToRole(String role, List<String> lifecycleBeanNames) {
|
||||
Assert.state(this.applicationContext != null, "An application context is required to use this method");
|
||||
for (String lifecycleBeanName : lifecycleBeanNames) {
|
||||
this.lazyLifecycles.add(role, lifecycleBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start all registered {@link SmartLifecycle}s in the role.
|
||||
* @param role the role.
|
||||
*/
|
||||
public void startLifecyclesInRole(String role) {
|
||||
if (this.lazyLifecycles.size() > 0) {
|
||||
addLazyLifecycles();
|
||||
}
|
||||
List<SmartLifecycle> lifecycles = this.lifecycles.get(role);
|
||||
if (lifecycles != null) {
|
||||
lifecycles = new ArrayList<SmartLifecycle>(lifecycles);
|
||||
Collections.sort(lifecycles, new Comparator<SmartLifecycle>() {
|
||||
|
||||
@Override
|
||||
public int compare(SmartLifecycle o1, SmartLifecycle o2) {
|
||||
return o1.getPhase() < o2.getPhase() ? -1
|
||||
: o1.getPhase() > o2.getPhase() ? 1 : 0;
|
||||
}
|
||||
|
||||
});
|
||||
for (SmartLifecycle lifecycle : lifecycles) {
|
||||
try {
|
||||
lifecycle.start();
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Failed to start " + lifecycle + " in role " + role);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all registered {@link SmartLifecycle}s in the role.
|
||||
* @param role the role.
|
||||
*/
|
||||
public void stopLifecyclesInRole(String role) {
|
||||
if (this.lazyLifecycles.size() > 0) {
|
||||
addLazyLifecycles();
|
||||
}
|
||||
List<SmartLifecycle> lifecycles = this.lifecycles.get(role);
|
||||
if (lifecycles != null) {
|
||||
lifecycles = new ArrayList<SmartLifecycle>(lifecycles);
|
||||
Collections.sort(lifecycles, new Comparator<SmartLifecycle>() {
|
||||
|
||||
@Override
|
||||
public int compare(SmartLifecycle o1, SmartLifecycle o2) {
|
||||
return o1.getPhase() < o2.getPhase() ? 1
|
||||
: o1.getPhase() > o2.getPhase() ? -1 : 0;
|
||||
}
|
||||
|
||||
});
|
||||
for (SmartLifecycle lifecycle : lifecycles) {
|
||||
try {
|
||||
lifecycle.stop();
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Failed to stop " + lifecycle + " in role " + role);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addLazyLifecycles() {
|
||||
for (Entry<String, List<String>> entry : this.lazyLifecycles.entrySet()) {
|
||||
doAddLifecyclesToRole(entry.getKey(), entry.getValue());
|
||||
}
|
||||
this.lazyLifecycles.clear();
|
||||
}
|
||||
|
||||
private void doAddLifecyclesToRole(String role, List<String> lifecycleBeanNames) {
|
||||
for (String lifecycleBeanName : lifecycleBeanNames) {
|
||||
try {
|
||||
SmartLifecycle lifecycle = this.applicationContext.getBean(lifecycleBeanName, SmartLifecycle.class);
|
||||
addLifecycleToRole(role, lifecycle);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
logger.warn("Skipped; no such bean :" + lifecycleBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AbstractLeaderEvent event) {
|
||||
if (event instanceof OnGrantedEvent) {
|
||||
startLifecyclesInRole(event.getRole());
|
||||
}
|
||||
else if (event instanceof OnRevokedEvent) {
|
||||
stopLifecyclesInRole(event.getRole());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4728,6 +4728,14 @@ default is 0. Values can be negative. See SmartLifeCycle.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="role" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Assigns this endpoint to a role. Endpoints in a role can be started/stopped as a group.
|
||||
See 'SmartLifecycleRoleController'.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:simpleType name="loggingLevel">
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?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:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<context:property-placeholder properties-ref="props" />
|
||||
|
||||
<util:properties id="props">
|
||||
<prop key="cluster.1">cluster</prop>
|
||||
<prop key="cluster.2">cluster</prop>
|
||||
<prop key="cluster.3">clusterX</prop>
|
||||
</util:properties>
|
||||
|
||||
<int:inbound-channel-adapter id="in" channel="foo" expression="'foo'" role="cluster" auto-startup="false">
|
||||
<int:poller fixed-delay="60000" />
|
||||
</int:inbound-channel-adapter>
|
||||
|
||||
<int:channel id="foo" />
|
||||
|
||||
<int:outbound-channel-adapter id="out1" channel="foo" role="cluster" auto-startup="false" ref="sink" method="foo" />
|
||||
|
||||
<int:outbound-channel-adapter id="out2" channel="foo" role="${cluster.1}" auto-startup="false" ref="sink" method="foo" />
|
||||
|
||||
<int:outbound-channel-adapter id="out3" channel="foo" role="${cluster.2}" auto-startup="false" ref="sink" method="foo" />
|
||||
|
||||
<int:outbound-channel-adapter id="out4" channel="foo" role="${cluster.3}" auto-startup="false" ref="sink" method="foo" />
|
||||
|
||||
<bean id="sink" class="org.springframework.integration.config.xml.EndpointRoleParserTests$Sink" />
|
||||
|
||||
<int:bridge id="bridge" input-channel="foo" output-channel="nullChannel" role="cluster" auto-startup="false" />
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.config.xml;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.leader.event.OnGrantedEvent;
|
||||
import org.springframework.integration.leader.event.OnRevokedEvent;
|
||||
import org.springframework.integration.support.SmartLifecycleRoleController;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.2
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class EndpointRoleParserTests {
|
||||
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter in;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer out1;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer out2;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer out3;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer out4;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer bridge;
|
||||
|
||||
@Autowired
|
||||
private SmartLifecycleRoleController controller;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
assertFalse(this.in.isRunning());
|
||||
assertFalse(this.out1.isRunning());
|
||||
assertFalse(this.out2.isRunning());
|
||||
assertFalse(this.out3.isRunning());
|
||||
assertFalse(this.out4.isRunning());
|
||||
assertFalse(this.bridge.isRunning());
|
||||
|
||||
this.controller.startLifecyclesInRole("cluster");
|
||||
|
||||
assertTrue(this.in.isRunning());
|
||||
assertTrue(this.out1.isRunning());
|
||||
assertTrue(this.out2.isRunning());
|
||||
assertTrue(this.out3.isRunning());
|
||||
assertFalse(this.out4.isRunning());
|
||||
assertTrue(this.bridge.isRunning());
|
||||
|
||||
this.controller.stopLifecyclesInRole("cluster");
|
||||
|
||||
assertFalse(this.in.isRunning());
|
||||
assertFalse(this.out1.isRunning());
|
||||
assertFalse(this.out2.isRunning());
|
||||
assertFalse(this.out3.isRunning());
|
||||
assertFalse(this.out4.isRunning());
|
||||
assertFalse(this.bridge.isRunning());
|
||||
|
||||
this.controller.onApplicationEvent(new OnGrantedEvent("foo", null, "cluster"));
|
||||
|
||||
assertTrue(this.in.isRunning());
|
||||
assertTrue(this.out1.isRunning());
|
||||
assertTrue(this.out2.isRunning());
|
||||
assertTrue(this.out3.isRunning());
|
||||
assertFalse(this.out4.isRunning());
|
||||
assertTrue(this.bridge.isRunning());
|
||||
|
||||
this.controller.onApplicationEvent(new OnRevokedEvent("foo", null, "cluster"));
|
||||
|
||||
assertFalse(this.in.isRunning());
|
||||
assertFalse(this.out1.isRunning());
|
||||
assertFalse(this.out2.isRunning());
|
||||
assertFalse(this.out3.isRunning());
|
||||
assertFalse(this.out4.isRunning());
|
||||
assertFalse(this.bridge.isRunning());
|
||||
}
|
||||
|
||||
public static class Sink {
|
||||
|
||||
public void foo(String s) {}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,6 +74,7 @@ import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.MessagingGateway;
|
||||
import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.annotation.Publisher;
|
||||
import org.springframework.integration.annotation.Role;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.annotation.Transformer;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
@@ -88,6 +89,7 @@ import org.springframework.integration.config.ExpressionControlBusFactoryBean;
|
||||
import org.springframework.integration.config.GlobalChannelInterceptor;
|
||||
import org.springframework.integration.config.IntegrationConverter;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.MethodInvokingMessageSource;
|
||||
import org.springframework.integration.endpoint.PollingConsumer;
|
||||
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
|
||||
@@ -96,6 +98,7 @@ import org.springframework.integration.history.MessageHistoryConfigurer;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.MutableMessageBuilder;
|
||||
import org.springframework.integration.support.SmartLifecycleRoleController;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -118,6 +121,7 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import reactor.Environment;
|
||||
import reactor.rx.Promise;
|
||||
@@ -140,6 +144,9 @@ public class EnableIntegrationTests {
|
||||
@Autowired
|
||||
private PollableChannel input;
|
||||
|
||||
@Autowired
|
||||
private SmartLifecycleRoleController roleController;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("annotationTestService.handle.serviceActivator")
|
||||
private PollingConsumer serviceActivatorEndpoint;
|
||||
@@ -252,6 +259,10 @@ public class EnableIntegrationTests {
|
||||
@Autowired
|
||||
private MessageChannel controlBusChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("enableIntegrationTests.ContextConfiguration2.sendAsyncHandler.serviceActivator")
|
||||
private AbstractEndpoint sendAsyncHandler;
|
||||
|
||||
@Test
|
||||
public void testAnnotatedServiceActivator() {
|
||||
assertEquals(10L, TestUtils.getPropertyValue(this.serviceActivatorEndpoint, "maxMessagesPerPoll"));
|
||||
@@ -588,6 +599,21 @@ public class EnableIntegrationTests {
|
||||
assertThat(integers, Matchers.<Integer>contains(2, 4, 6, 8, 10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles() {
|
||||
this.roleController.stopLifecyclesInRole("foo");
|
||||
@SuppressWarnings("unchecked")
|
||||
MultiValueMap<String, SmartLifecycle> lifecycles = TestUtils.getPropertyValue(this.roleController,
|
||||
"lifecycles", MultiValueMap.class);
|
||||
assertEquals(2, lifecycles.size());
|
||||
assertEquals(2, lifecycles.get("foo").size());
|
||||
assertEquals(1, lifecycles.get("bar").size());
|
||||
assertFalse(this.serviceActivatorEndpoint.isRunning());
|
||||
assertFalse(this.sendAsyncHandler.isRunning());
|
||||
assertEquals(2, lifecycles.size());
|
||||
assertEquals(2, lifecycles.get("foo").size());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@IntegrationComponentScan
|
||||
@@ -849,6 +875,7 @@ public class EnableIntegrationTests {
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "sendAsyncChannel")
|
||||
@Role("foo")
|
||||
public MessageHandler sendAsyncHandler() {
|
||||
return new MessageHandler() {
|
||||
|
||||
@@ -863,6 +890,7 @@ public class EnableIntegrationTests {
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "controlBusChannel")
|
||||
@Role("bar")
|
||||
public ExpressionControlBusFactoryBean controlBus() throws Exception {
|
||||
return new ExpressionControlBusFactoryBean();
|
||||
}
|
||||
@@ -1031,6 +1059,7 @@ public class EnableIntegrationTests {
|
||||
poller = @Poller(maxMessagesPerPoll = "${poller.maxMessagesPerPoll}", fixedDelay = "${poller.interval}"))
|
||||
@Publisher
|
||||
@Payload("#args[0].toLowerCase()")
|
||||
@Role("foo")
|
||||
public String handle(String payload) {
|
||||
return payload.toUpperCase();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.2
|
||||
*
|
||||
*/
|
||||
public class SmartLifecycleRoleControllerTests {
|
||||
|
||||
@Test
|
||||
public void testOrder() {
|
||||
SmartLifecycle lc1 = mock(SmartLifecycle.class);
|
||||
when(lc1.getPhase()).thenReturn(2);
|
||||
SmartLifecycle lc2 = mock(SmartLifecycle.class);
|
||||
when(lc1.getPhase()).thenReturn(1);
|
||||
MultiValueMap<String , SmartLifecycle> map = new LinkedMultiValueMap<String, SmartLifecycle>();
|
||||
map.add("foo", lc1);
|
||||
map.add("foo", lc2);
|
||||
SmartLifecycleRoleController controller = new SmartLifecycleRoleController(map);
|
||||
controller.startLifecyclesInRole("foo");
|
||||
controller.stopLifecyclesInRole("foo");
|
||||
InOrder inOrder = inOrder(lc1, lc2);
|
||||
inOrder.verify(lc2).start();
|
||||
inOrder.verify(lc1).start();
|
||||
inOrder.verify(lc1).stop();
|
||||
inOrder.verify(lc2).stop();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user