/*
* Copyright 2013-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.
* 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.cloud.bus;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.bus.endpoint.BusEndpoint;
import org.springframework.cloud.bus.endpoint.EnvironmentBusEndpoint;
import org.springframework.cloud.bus.endpoint.RefreshBusEndpoint;
import org.springframework.cloud.bus.event.AckRemoteApplicationEvent;
import org.springframework.cloud.bus.event.EnvironmentChangeListener;
import org.springframework.cloud.bus.event.RefreshListener;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
import org.springframework.cloud.bus.event.SentApplicationEvent;
import org.springframework.cloud.bus.event.TraceListener;
import org.springframework.cloud.context.environment.EnvironmentManager;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.config.BindingProperties;
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
* @author Spencer Gibb
* @author Dave Syer
*/
@Configuration
@ConditionalOnBusEnabled
@EnableBinding(SpringCloudBusClient.class)
@EnableConfigurationProperties(BusProperties.class)
public class BusAutoConfiguration implements ApplicationEventPublisherAware {
public static final String BUS_PATH_MATCHER_NAME = "busPathMatcher";
@Autowired
@Output(SpringCloudBusClient.OUTPUT)
private MessageChannel cloudBusOutboundChannel;
@Autowired
private ServiceMatcher serviceMatcher;
@Autowired
private ChannelBindingServiceProperties bindings;
@Autowired
private BusProperties bus;
private ApplicationEventPublisher applicationEventPublisher;
@PostConstruct
public void init() {
BindingProperties inputBinding = this.bindings.getBindings()
.get(SpringCloudBusClient.INPUT);
if (inputBinding == null) {
this.bindings.getBindings().put(SpringCloudBusClient.INPUT,
new BindingProperties());
}
BindingProperties input = this.bindings.getBindings()
.get(SpringCloudBusClient.INPUT);
if (input.getDestination() == null) {
input.setDestination(this.bus.getDestination());
}
BindingProperties outputBinding = this.bindings.getBindings()
.get(SpringCloudBusClient.OUTPUT);
if (outputBinding == null) {
this.bindings.getBindings().put(SpringCloudBusClient.OUTPUT,
new BindingProperties());
}
BindingProperties output = this.bindings.getBindings()
.get(SpringCloudBusClient.OUTPUT);
if (output.getDestination() == null) {
output.setDestination(this.bus.getDestination());
}
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@EventListener(classes = RemoteApplicationEvent.class)
public void acceptLocal(RemoteApplicationEvent event) {
if (this.serviceMatcher.isFromSelf(event)
&& !(event instanceof AckRemoteApplicationEvent)) {
this.cloudBusOutboundChannel.send(MessageBuilder.withPayload(event).build());
}
}
@StreamListener(SpringCloudBusClient.INPUT)
public void acceptRemote(RemoteApplicationEvent event) {
if (event instanceof AckRemoteApplicationEvent) {
if (this.bus.getTrace().isEnabled() && !this.serviceMatcher.isFromSelf(event)
&& this.applicationEventPublisher != null) {
this.applicationEventPublisher.publishEvent(event);
}
// If it's an ACK we are finished processing at this point
return;
}
if (this.serviceMatcher.isForSelf(event)
&& this.applicationEventPublisher != null) {
if (!this.serviceMatcher.isFromSelf(event)) {
this.applicationEventPublisher.publishEvent(event);
}
if (this.bus.getAck().isEnabled()) {
AckRemoteApplicationEvent ack = new AckRemoteApplicationEvent(this,
this.serviceMatcher.getServiceId(),
this.bus.getAck().getDestinationService(),
event.getDestinationService(), event.getId(), event.getClass());
this.cloudBusOutboundChannel
.send(MessageBuilder.withPayload(ack).build());
this.applicationEventPublisher.publishEvent(ack);
}
}
if (this.bus.getTrace().isEnabled() && this.applicationEventPublisher != null) {
// We are set to register sent events so publish it for local consumption,
// irrespective of the origin
this.applicationEventPublisher.publishEvent(new SentApplicationEvent(this,
event.getOriginService(), event.getDestinationService(),
event.getId(), event.getClass()));
}
}
@Configuration
protected static class MatcherConfiguration {
@BusPathMatcher
// There is a @Bean of type PathMatcher coming from Spring MVC
@ConditionalOnMissingBean(name = BusAutoConfiguration.BUS_PATH_MATCHER_NAME)
@Bean(name = BusAutoConfiguration.BUS_PATH_MATCHER_NAME)
public PathMatcher busPathMatcher() {
return new DefaultBusPathMatcher(new AntPathMatcher(":"));
}
@Bean
public ServiceMatcher serviceMatcher(@BusPathMatcher PathMatcher pathMatcher) {
ServiceMatcher serviceMatcher = new ServiceMatcher();
serviceMatcher.setMatcher(pathMatcher);
return serviceMatcher;
}
}
@Configuration
@ConditionalOnClass(Endpoint.class)
protected static class BusEndpointConfiguration {
@Bean
public BusEndpoint busEndpoint() {
return new BusEndpoint();
}
}
@Configuration
@ConditionalOnClass({ Endpoint.class, RefreshScope.class })
@ConditionalOnBean(ContextRefresher.class)
protected static class BusRefreshConfiguration {
@Bean
@ConditionalOnProperty(value = "spring.cloud.bus.refresh.enabled", matchIfMissing = true)
public RefreshListener refreshListener(ContextRefresher contextRefresher) {
return new RefreshListener(contextRefresher);
}
@Configuration
@ConditionalOnProperty(value = "endpoints.spring.cloud.bus.refresh.enabled", matchIfMissing = true)
protected static class BusRefreshEndpointConfiguration {
@Bean
public RefreshBusEndpoint refreshBusEndpoint(ApplicationContext context,
BusEndpoint busEndpoint) {
return new RefreshBusEndpoint(context, context.getId(), busEndpoint);
}
}
}
@Configuration
@ConditionalOnClass({ Endpoint.class })
@ConditionalOnBean(TraceRepository.class)
@ConditionalOnProperty(value = "spring.cloud.bus.trace.enabled", matchIfMissing = false)
protected static class BusAckTraceConfiguration {
@Bean
@ConditionalOnMissingBean
public TraceListener ackTraceListener(TraceRepository repository) {
return new TraceListener(repository);
}
}
@Configuration
@ConditionalOnClass(EnvironmentManager.class)
@ConditionalOnBean(EnvironmentManager.class)
protected static class BusEnvironmentConfiguration {
@Bean
@ConditionalOnProperty(value = "spring.cloud.bus.env.enabled", matchIfMissing = true)
public EnvironmentChangeListener environmentChangeListener() {
return new EnvironmentChangeListener();
}
@Configuration
@ConditionalOnClass(Endpoint.class)
@ConditionalOnProperty(value = "endpoints.spring.cloud.bus.env.enabled", matchIfMissing = true)
protected static class EnvironmentBusEndpointConfiguration {
@Bean
public EnvironmentBusEndpoint environmentBusEndpoint(
ApplicationContext context, BusEndpoint busEndpoint) {
return new EnvironmentBusEndpoint(context, context.getId(), busEndpoint);
}
}
}
}