/*
* 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.stream.binding;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.cloud.stream.binder.Binding;
import org.springframework.cloud.stream.config.BindingServiceProperties;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.core.BeanFactoryMessageChannelDestinationResolver;
import org.springframework.messaging.core.DestinationResolutionException;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* A {@link org.springframework.messaging.core.DestinationResolver} implementation that
* resolves the channel from the bean factory and, if not present, creates a new channel
* and adds it to the factory after binding it to the binder.
*
* @author Mark Fisher
* @author Gary Russell
* @author Ilayaperumal Gopinathan
*/
public class BinderAwareChannelResolver extends BeanFactoryMessageChannelDestinationResolver {
private final BindingService bindingService;
private final AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory;
private final DynamicDestinationsBindable dynamicDestinationsBindable;
private ConfigurableListableBeanFactory beanFactory;
public BinderAwareChannelResolver(BindingService bindingService,
AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory,
DynamicDestinationsBindable dynamicDestinationsBindable) {
this.dynamicDestinationsBindable = dynamicDestinationsBindable;
Assert.notNull(bindingService, "'bindingService' cannot be null");
Assert.notNull(bindingTargetFactory, "'bindingTargetFactory' cannot be null");
this.bindingService = bindingService;
this.bindingTargetFactory = bindingTargetFactory;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@Override
public MessageChannel resolveDestination(String channelName) {
try {
return super.resolveDestination(channelName);
}
catch (DestinationResolutionException e) {
// intentionally empty; will check again while holding the monitor
}
synchronized (this) {
DestinationResolutionException destinationResolutionException;
try {
return super.resolveDestination(channelName);
}
catch (DestinationResolutionException e) {
destinationResolutionException = e;
}
MessageChannel channel = null;
if (this.beanFactory != null) {
String[] dynamicDestinations = null;
BindingServiceProperties bindingServiceProperties = this.bindingService
.getBindingServiceProperties();
if (bindingServiceProperties != null) {
dynamicDestinations = bindingServiceProperties.getDynamicDestinations();
}
boolean dynamicAllowed = ObjectUtils.isEmpty(dynamicDestinations)
|| ObjectUtils.containsElement(dynamicDestinations, channelName);
if (dynamicAllowed) {
channel = this.bindingTargetFactory.createOutput(channelName);
this.beanFactory.registerSingleton(channelName, channel);
channel = (MessageChannel) this.beanFactory.initializeBean(channel, channelName);
Binding<MessageChannel> binding = this.bindingService.bindProducer(channel, channelName);
this.dynamicDestinationsBindable.addOutputBinding(channelName, binding);
}
else {
throw destinationResolutionException;
}
}
return channel;
}
}
}