renamed modules org.springframework.integration.* -> spring-integration-*
@Ignore'd SimpleTcpNetOutboundGatewayTests#testOutboundClose() to avoid failure; this failure is correlated to the module name change, but hard to understand how it would be caused by it
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.core.MessageHeaders;
|
||||
import org.springframework.integration.message.MessageBuilder;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base class for MessageGroupProcessor implementations that aggregate the group of Messages into a single Message.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Alexander Peters
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractAggregatingMessageGroupProcessor implements MessageGroupProcessor {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate,
|
||||
MessageChannel outputChannel) {
|
||||
Assert.notNull(group, "MessageGroup must not be null");
|
||||
Assert.notNull(outputChannel, "'outputChannel' must not be null");
|
||||
Object payload = this.aggregatePayloads(group);
|
||||
Map<String, Object> headers = this.aggregateHeaders(group);
|
||||
MessageBuilder<?> builder = (payload instanceof Message) ? MessageBuilder.fromMessage((Message<?>) payload)
|
||||
: MessageBuilder.withPayload(payload);
|
||||
Message<?> message = builder.copyHeadersIfAbsent(headers).build();
|
||||
channelTemplate.send(message, outputChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* This default implementation simply returns all headers that have no conflicts among the group. An absent header
|
||||
* on one or more Messages within the group is not considered a conflict. Subclasses may override this method with
|
||||
* more advanced conflict-resolution strategies if necessary.
|
||||
*/
|
||||
protected Map<String, Object> aggregateHeaders(MessageGroup group) {
|
||||
Map<String, Object> aggregatedHeaders = new HashMap<String, Object>();
|
||||
Set<String> conflictKeys = new HashSet<String>();
|
||||
for (Message<?> message : group.getUnmarked()) {
|
||||
MessageHeaders currentHeaders = message.getHeaders();
|
||||
for (String key : currentHeaders.keySet()) {
|
||||
if (MessageHeaders.ID.equals(key) || MessageHeaders.TIMESTAMP.equals(key)
|
||||
|| MessageHeaders.SEQUENCE_SIZE.equals(key)) {
|
||||
continue;
|
||||
}
|
||||
Object value = currentHeaders.get(key);
|
||||
if (!aggregatedHeaders.containsKey(key)) {
|
||||
aggregatedHeaders.put(key, value);
|
||||
} else if (!value.equals(aggregatedHeaders.get(key))) {
|
||||
conflictKeys.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String keyToRemove : conflictKeys) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Excluding header '" + keyToRemove + "' upon aggregation due to conflict(s) "
|
||||
+ "in MessageGroup with correlation key: " + group.getCorrelationKey());
|
||||
}
|
||||
aggregatedHeaders.remove(keyToRemove);
|
||||
}
|
||||
return aggregatedHeaders;
|
||||
}
|
||||
|
||||
protected abstract Object aggregatePayloads(MessageGroup group);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.integration.channel.ChannelResolver;
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.channel.NullChannel;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.core.MessageHeaders;
|
||||
import org.springframework.integration.core.MessageProducer;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageGroupCallback;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Message handler that holds a buffer of correlated messages in a
|
||||
* {@link MessageStore}. This class takes care of correlated groups of messages
|
||||
* that can be completed in batches. It is useful for aggregating, resequencing,
|
||||
* or custom implementations requiring correlation.
|
||||
* <p/>
|
||||
* To customize this handler inject {@link CorrelationStrategy},
|
||||
* {@link ReleaseStrategy}, and {@link MessageGroupProcessor} implementations as
|
||||
* you require.
|
||||
* <p/>
|
||||
* By default the CorrelationStrategy will be a
|
||||
* HeaderAttributeCorrelationStrategy and the ReleaseStrategy will be a
|
||||
* SequenceSizeReleaseStrategy.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Dave Syer
|
||||
* @since 2.0
|
||||
*/
|
||||
public class CorrelatingMessageHandler extends AbstractMessageHandler implements
|
||||
MessageProducer {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(CorrelatingMessageHandler.class);
|
||||
|
||||
public static final long DEFAULT_SEND_TIMEOUT = 1000L;
|
||||
|
||||
public static final long DEFAULT_REAPER_INTERVAL = 1000L;
|
||||
|
||||
public static final long DEFAULT_TIMEOUT = 60000L;
|
||||
|
||||
private MessageGroupStore messageStore;
|
||||
|
||||
private final MessageGroupProcessor outputProcessor;
|
||||
|
||||
private volatile CorrelationStrategy correlationStrategy;
|
||||
|
||||
private volatile ReleaseStrategy releaseStrategy;
|
||||
|
||||
private MessageChannel outputChannel;
|
||||
|
||||
private final MessageChannelTemplate channelTemplate = new MessageChannelTemplate();
|
||||
|
||||
private volatile MessageChannel discardChannel = new NullChannel();
|
||||
|
||||
private boolean sendPartialResultOnExpiry = false;
|
||||
|
||||
private final ConcurrentMap<Object, Object> locks = new ConcurrentHashMap<Object, Object>();
|
||||
|
||||
public CorrelatingMessageHandler(MessageGroupProcessor processor,
|
||||
MessageGroupStore store, CorrelationStrategy correlationStrategy,
|
||||
ReleaseStrategy releaseStrategy) {
|
||||
Assert.notNull(store);
|
||||
Assert.notNull(processor);
|
||||
setMessageStore(store);
|
||||
this.outputProcessor = processor;
|
||||
this.correlationStrategy = correlationStrategy == null ? new HeaderAttributeCorrelationStrategy(
|
||||
MessageHeaders.CORRELATION_ID)
|
||||
: correlationStrategy;
|
||||
this.releaseStrategy = releaseStrategy == null ? new SequenceSizeReleaseStrategy()
|
||||
: releaseStrategy;
|
||||
this.channelTemplate.setSendTimeout(DEFAULT_SEND_TIMEOUT);
|
||||
}
|
||||
|
||||
public CorrelatingMessageHandler(MessageGroupProcessor processor,
|
||||
MessageGroupStore store) {
|
||||
this(processor, store, null, null);
|
||||
}
|
||||
|
||||
public CorrelatingMessageHandler(MessageGroupProcessor processor) {
|
||||
this(processor, new SimpleMessageStore(0), null, null);
|
||||
}
|
||||
|
||||
public void setMessageStore(MessageGroupStore store) {
|
||||
this.messageStore = store;
|
||||
store.registerMessageGroupExpiryCallback(new MessageGroupCallback() {
|
||||
public void execute(MessageGroup group) {
|
||||
forceComplete(group);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setCorrelationStrategy(CorrelationStrategy correlationStrategy) {
|
||||
Assert.notNull(correlationStrategy);
|
||||
this.correlationStrategy = correlationStrategy;
|
||||
}
|
||||
|
||||
public void setReleaseStrategy(ReleaseStrategy releaseStrategy) {
|
||||
Assert.notNull(releaseStrategy);
|
||||
this.releaseStrategy = releaseStrategy;
|
||||
}
|
||||
|
||||
public void setOutputChannel(MessageChannel outputChannel) {
|
||||
Assert.notNull(outputChannel, "'outputChannel' must not be null");
|
||||
this.outputChannel = outputChannel;
|
||||
}
|
||||
|
||||
public void setChannelResolver(ChannelResolver channelResolver) {
|
||||
super.setChannelResolver(channelResolver);
|
||||
}
|
||||
|
||||
public void setDiscardChannel(MessageChannel discardChannel) {
|
||||
this.discardChannel = discardChannel;
|
||||
}
|
||||
|
||||
public void setSendTimeout(long sendTimeout) {
|
||||
this.channelTemplate.setSendTimeout(sendTimeout);
|
||||
}
|
||||
|
||||
public void setSendPartialResultOnExpiry(boolean sendPartialResultOnExpiry) {
|
||||
this.sendPartialResultOnExpiry = sendPartialResultOnExpiry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "aggregator";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessageInternal(Message<?> message) throws Exception {
|
||||
|
||||
Object correlationKey = correlationStrategy.getCorrelationKey(message);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Handling message with correlationKey ["
|
||||
+ correlationKey + "]: " + message);
|
||||
}
|
||||
|
||||
// TODO: INT-1117 - make the lock global?
|
||||
Object lock = getLock(correlationKey);
|
||||
synchronized (lock) {
|
||||
|
||||
MessageGroup group = messageStore.getMessageGroup(correlationKey);
|
||||
|
||||
if (group.add(message)) {
|
||||
|
||||
store(correlationKey, message);
|
||||
|
||||
if (releaseStrategy.canRelease(group)) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Completing group with correlationKey ["
|
||||
+ correlationKey + "]");
|
||||
}
|
||||
try {
|
||||
outputProcessor.processAndSend(group, channelTemplate,
|
||||
this.resolveReplyChannel(message,
|
||||
this.outputChannel));
|
||||
} finally {
|
||||
|
||||
// Always clean up even if there was an exception
|
||||
// processing messages
|
||||
if (group.isComplete() || group.getSequenceSize() == 0) {
|
||||
// The group is complete or else there is no
|
||||
// sequence so there is no more state to track
|
||||
remove(group);
|
||||
} else {
|
||||
// Mark these messages as processed, but do not
|
||||
// remove the group from store
|
||||
mark(group);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (group.isComplete()) {
|
||||
|
||||
try {
|
||||
// If not releasing any messages the group might still
|
||||
// be complete
|
||||
for (Message<?> discard : group.getUnmarked()) {
|
||||
discardChannel.send(discard);
|
||||
}
|
||||
} finally {
|
||||
remove(group);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
discardChannel.send(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final boolean forceComplete(MessageGroup group) {
|
||||
|
||||
Object correlationKey = group.getCorrelationKey();
|
||||
Object lock = getLock(correlationKey);
|
||||
synchronized (lock) {
|
||||
|
||||
if (group.size() > 0) {
|
||||
// last chance for normal completion
|
||||
try {
|
||||
if (releaseStrategy.canRelease(group)) {
|
||||
outputProcessor.processAndSend(group, channelTemplate,
|
||||
resolveReplyChannel(group.getOne(),
|
||||
this.outputChannel));
|
||||
} else {
|
||||
if (sendPartialResultOnExpiry) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger
|
||||
.info("Processing partially complete messages for key ["
|
||||
+ correlationKey
|
||||
+ "] to: "
|
||||
+ outputChannel);
|
||||
}
|
||||
outputProcessor.processAndSend(group,
|
||||
channelTemplate, resolveReplyChannel(group
|
||||
.getOne(), this.outputChannel));
|
||||
} else {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger
|
||||
.info("Discarding partially complete messages for key ["
|
||||
+ correlationKey
|
||||
+ "] to: "
|
||||
+ discardChannel);
|
||||
}
|
||||
for (Message<?> message : group.getUnmarked()) {
|
||||
discardChannel.send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
remove(group);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Object getLock(Object correlationKey) {
|
||||
locks.putIfAbsent(correlationKey, correlationKey);
|
||||
return locks.get(correlationKey);
|
||||
}
|
||||
|
||||
private void mark(MessageGroup group) {
|
||||
messageStore.markMessageGroup(group);
|
||||
}
|
||||
|
||||
private void remove(MessageGroup group) {
|
||||
Object correlationKey = group.getCorrelationKey();
|
||||
messageStore.removeMessageGroup(correlationKey);
|
||||
locks.remove(correlationKey);
|
||||
}
|
||||
|
||||
private void store(Object correlationKey, Message<?> message) {
|
||||
messageStore.addMessageToGroup(correlationKey, message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* Strategy for determining how messages shall be correlated. Implementations
|
||||
* shall return the correlation key value associated with a particular message.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public interface CorrelationStrategy {
|
||||
|
||||
Object getCorrelationKey(Message<?> message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.aggregator;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.handler.MethodInvokingMessageProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link CorrelationStrategy} implementation that works as an adapter to another bean.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class CorrelationStrategyAdapter implements CorrelationStrategy {
|
||||
|
||||
private final MethodInvokingMessageProcessor processor;
|
||||
|
||||
|
||||
public CorrelationStrategyAdapter(Object object, String methodName) {
|
||||
this.processor = new MethodInvokingMessageProcessor(object, methodName, true);
|
||||
}
|
||||
|
||||
public CorrelationStrategyAdapter(Object object, Method method) {
|
||||
Assert.notNull(object, "'object' must not be null");
|
||||
Assert.notNull(method, "'method' must not be null");
|
||||
Assert.isTrue(method.getParameterTypes().length == 1, "Method must accept exactly one parameter");
|
||||
Assert.isTrue(!Void.TYPE.equals(method.getReturnType()), "Method return type must not be void");
|
||||
this.processor = new MethodInvokingMessageProcessor(object, method);
|
||||
}
|
||||
|
||||
public Object getCorrelationKey(Message<?> message) {
|
||||
return processor.processMessage(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This implementation of MessageGroupProcessor will take the messages from the
|
||||
* MessageGroup and pass them on in a single message with a Collection as a payload.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Alexander Peters
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultAggregatingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
|
||||
|
||||
@Override
|
||||
protected final Object aggregatePayloads(MessageGroup group) {
|
||||
Collection<Message<?>> messages = group.getUnmarked();
|
||||
Assert.notEmpty(messages, this.getClass().getSimpleName() + " cannot process empty message groups");
|
||||
List<Object> payloads = new ArrayList<Object>(messages.size());
|
||||
for (Message<?> message : messages) {
|
||||
payloads.add(message.getPayload());
|
||||
}
|
||||
return payloads;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link CorrelationStrategy}. Uses a header
|
||||
* attribute to determine the correlation key value.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class HeaderAttributeCorrelationStrategy implements CorrelationStrategy {
|
||||
|
||||
private String attributeName;
|
||||
|
||||
|
||||
public HeaderAttributeCorrelationStrategy(String attributeName) {
|
||||
this.attributeName = attributeName;
|
||||
}
|
||||
|
||||
|
||||
public Object getCorrelationKey(Message<?> message) {
|
||||
return message.getHeaders().get(this.attributeName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
/**
|
||||
* A {@link ReleaseStrategy} that releases only the first <code>n</code> messages, where <code>n</code> is a threshold.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class MessageCountReleaseStrategy implements ReleaseStrategy {
|
||||
|
||||
private final int threshold;
|
||||
|
||||
/**
|
||||
* @param threshold the number of messages to accept before releasing
|
||||
*/
|
||||
public MessageCountReleaseStrategy(int threshold) {
|
||||
super();
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor is only one message is required (threshold=1).
|
||||
*/
|
||||
public MessageCountReleaseStrategy() {
|
||||
this(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the group if it has more messages than the threshold and has not previously been released. Previous
|
||||
* releases leave an imprint on the group in the form of marked messages. It is possible that more messages than the
|
||||
* threshold could be released, but only if multiple consumers receive messages from the same group concurrently.
|
||||
*/
|
||||
public boolean canRelease(MessageGroup group) {
|
||||
return group.size() >= threshold && group.getMarked().size() == 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.springframework.integration.aggregator;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
/**
|
||||
* A processor for <i>correlated</i> groups of messages.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @see org.springframework.integration.aggregator.CorrelatingMessageHandler
|
||||
*/
|
||||
public interface MessageGroupProcessor {
|
||||
|
||||
/**
|
||||
* Process the given group and send the resulting message(s) to the output channel using the channel template.
|
||||
* Implementations are free to send as little or as many messages based on the invocation as needed. For example an
|
||||
* aggregating processor will send only a single message representing the group, where a resequencing strategy will
|
||||
* send all messages in the group individually.
|
||||
*/
|
||||
void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate, MessageChannel outputChannel);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.aggregator;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessagingException;
|
||||
import org.springframework.integration.util.DefaultMethodInvoker;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for implementing adapters for methods which take as an argument a
|
||||
* list of {@link Message Message} instances or payloads.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class MessageListMethodAdapter {
|
||||
|
||||
private final DefaultMethodInvoker invoker;
|
||||
|
||||
protected final Method method;
|
||||
|
||||
|
||||
public MessageListMethodAdapter(Object object, String methodName) {
|
||||
Assert.notNull(object, "'object' must not be null");
|
||||
Assert.notNull(methodName, "'methodName' must not be null");
|
||||
this.method = ReflectionUtils.findMethod(object.getClass(), methodName, new Class<?>[]{List.class});
|
||||
Assert.notNull(this.method, "Method '" + methodName +
|
||||
"(List<?> args)' not found on '" + object.getClass().getName() + "'.");
|
||||
this.invoker = new DefaultMethodInvoker(object, this.method);
|
||||
}
|
||||
|
||||
public MessageListMethodAdapter(Object object, Method method) {
|
||||
Assert.notNull(object, "'object' must not be null");
|
||||
Assert.notNull(method, "'method' must not be null");
|
||||
Assert.isTrue(method.getParameterTypes().length == 1
|
||||
&& method.getParameterTypes()[0].equals(List.class),
|
||||
"Method " + method + " does not accept exactly one parameter, of type List.");
|
||||
this.method = method;
|
||||
this.invoker = new DefaultMethodInvoker(object, this.method);
|
||||
}
|
||||
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
private static boolean isActualTypeParameterizedMessage(Method method) {
|
||||
return (getCollectionActualType(method) instanceof ParameterizedType)
|
||||
&& Message.class.isAssignableFrom((Class<?>) ((ParameterizedType) getCollectionActualType(method)).getRawType());
|
||||
}
|
||||
|
||||
protected final Object executeMethod(Collection<? extends Message<?>> messages) {
|
||||
try {
|
||||
if (isMethodParameterParameterized(this.method) && isHavingActualTypeArguments(this.method)
|
||||
&& (isActualTypeRawMessage(this.method) || isActualTypeParameterizedMessage(this.method))) {
|
||||
return this.invoker.invokeMethod(messages);
|
||||
}
|
||||
return this.invoker.invokeMethod(extractPayloadsFromMessages(messages));
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new MessagingException(
|
||||
"Method '" + this.method + "' threw an Exception.", e.getTargetException());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException("Failed to invoke method '" + this.method + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
private List<?> extractPayloadsFromMessages(Collection<? extends Message<?>> messages) {
|
||||
List<Object> payloadList = new ArrayList<Object>();
|
||||
for (Message<?> message : messages) {
|
||||
payloadList.add(message.getPayload());
|
||||
}
|
||||
return payloadList;
|
||||
}
|
||||
|
||||
private static boolean isActualTypeRawMessage(Method method) {
|
||||
return getCollectionActualType(method).equals(Message.class);
|
||||
}
|
||||
|
||||
private static Type getCollectionActualType(Method method) {
|
||||
return ((ParameterizedType) method.getGenericParameterTypes()[0]).getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
private static boolean isHavingActualTypeArguments(Method method) {
|
||||
return ((ParameterizedType) method.getGenericParameterTypes()[0]).getActualTypeArguments().length == 1;
|
||||
}
|
||||
|
||||
private static boolean isMethodParameterParameterized(Method method) {
|
||||
return method.getGenericParameterTypes().length == 1
|
||||
&& method.getGenericParameterTypes()[0] instanceof ParameterizedType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.aggregator;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* A {@link Comparator} implementation based on the 'sequence number'
|
||||
* property of a {@link Message Message's} header.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MessageSequenceComparator implements Comparator<Message<?>> {
|
||||
|
||||
public int compare(Message<?> message1, Message<?> message2) {
|
||||
Integer s1 = message1.getHeaders().getSequenceNumber();
|
||||
Integer s2 = message2.getHeaders().getSequenceNumber();
|
||||
if (s1 == null) {
|
||||
s1 = 0;
|
||||
}
|
||||
if (s2 == null) {
|
||||
s2 = 0;
|
||||
}
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.Header;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* MessageGroupProcessor that serves as an adapter for the invocation of a POJO method.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MethodInvokingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
|
||||
|
||||
private final MessageListMethodAdapter adapter;
|
||||
|
||||
/**
|
||||
* Creates a wrapper around the target passed in. This constructor will choose the best fitting method and throw an
|
||||
* exception when methods are ambiguous or no fitting methods can be found.
|
||||
*
|
||||
* @param target the object to wrap
|
||||
* @throws IllegalStateException when no single method can be found unambiguously
|
||||
*/
|
||||
public MethodInvokingMessageGroupProcessor(Object target) {
|
||||
this.adapter = new MessageListMethodAdapter(target, this.findAggregatorMethod(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper around the object passed in. This constructor will look for a named method specifically and
|
||||
* fail when it cannot find a method with the given name.
|
||||
*
|
||||
* @param target the object to wrap
|
||||
* @param method the name of the method to look for
|
||||
*/
|
||||
public MethodInvokingMessageGroupProcessor(Object target, String method) {
|
||||
this.adapter = new MessageListMethodAdapter(target, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Object aggregatePayloads(MessageGroup group) {
|
||||
final Collection<Message<?>> messagesUpForProcessing = group.getUnmarked();
|
||||
Object result = this.adapter.executeMethod(messagesUpForProcessing);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Method findAggregatorMethod(Object candidate) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(candidate);
|
||||
if (targetClass == null) {
|
||||
targetClass = candidate.getClass();
|
||||
}
|
||||
Method method = this.findAnnotatedMethod(targetClass);
|
||||
if (method == null) {
|
||||
method = this.findSinglePublicMethod(targetClass);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private Method findAnnotatedMethod(final Class<?> targetClass) {
|
||||
final AtomicReference<Method> annotatedMethod = new AtomicReference<Method>();
|
||||
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = AnnotationUtils.findAnnotation(method, Aggregator.class);
|
||||
if (annotation != null) {
|
||||
Assert.isNull(annotatedMethod.get(), "found more than one method on target class [" + targetClass
|
||||
+ "] with the annotation type [" + Aggregator.class.getName() + "]");
|
||||
annotatedMethod.set(method);
|
||||
}
|
||||
}
|
||||
});
|
||||
return annotatedMethod.get();
|
||||
}
|
||||
|
||||
private Method findSinglePublicMethod(Class<?> targetClass) {
|
||||
Set<Method> methods = new HashSet<Method>();
|
||||
for (Method method : targetClass.getMethods()) {
|
||||
if (!method.getDeclaringClass().equals(Object.class)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
removeListIncompatibleMethodsFrom(methods);
|
||||
removeVoidMethodsFrom(methods);
|
||||
removeUnfittingFrom(methods);
|
||||
if (methods.size() > 1) {
|
||||
throw new IllegalArgumentException("Class [" + targetClass + "] contains more than one public Method.");
|
||||
}
|
||||
return methods.isEmpty() ? null : methods.iterator().next();
|
||||
}
|
||||
|
||||
private void removeListIncompatibleMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
int found = 0;
|
||||
for (Class<?> parameterClass : method.getParameterTypes()) {
|
||||
if (Collection.class.isAssignableFrom(parameterClass)) {
|
||||
found++;
|
||||
}
|
||||
}
|
||||
return found != 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeVoidMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
return method.getReturnType().getName().equals("void");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Method> removeUnfittingFrom(Set<Method> candidates) {
|
||||
return removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
return (!isFittinglyAnnotated(parameterTypes, parameterAnnotations));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isFittinglyAnnotated(Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) {
|
||||
int candidateParametersFound = 0;
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
Class<?> parameterType = parameterTypes[i];
|
||||
if (Collection.class.isAssignableFrom(parameterType)) {
|
||||
boolean headerAnnotationFound = false;
|
||||
for (Annotation annotation : parameterAnnotations[i]) {
|
||||
if (annotation instanceof Header) {
|
||||
headerAnnotationFound = true;
|
||||
}
|
||||
}
|
||||
if (!headerAnnotationFound) {
|
||||
candidateParametersFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidateParametersFound == 1;
|
||||
}
|
||||
|
||||
private Set<Method> removeMethodsMatchingSelector(Set<Method> candidates, MethodSelector selector) {
|
||||
Set<Method> removed = new HashSet<Method>();
|
||||
Iterator<Method> iterator = candidates.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Method method = iterator.next();
|
||||
if (selector.select(method)) {
|
||||
iterator.remove();
|
||||
removed.add(method);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
private interface MethodSelector {
|
||||
boolean select(Method method);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.integration.aggregator;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
/**
|
||||
* This implementation of MessageGroupProcessor will forward all messages inside the group to the given output channel.
|
||||
* This is useful if there is no requirement to process the messages, but they should just be blocked as a group until
|
||||
* their ReleaseStrategy lets them pass through.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class PassThroughMessageGroupProcessor implements MessageGroupProcessor {
|
||||
|
||||
public void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate, MessageChannel outputChannel) {
|
||||
for (Message<?> message : group.getUnmarked()) {
|
||||
channelTemplate.send(message, outputChannel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.aggregator;
|
||||
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Strategy for determining when a group of messages reaches a state of
|
||||
* completion (i.e. can trip a barrier).
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public interface ReleaseStrategy {
|
||||
|
||||
boolean canRelease(MessageGroup group);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.aggregator;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapter for methods annotated with
|
||||
* {@link org.springframework.integration.annotation.ReleaseStrategy @ReleaseStrategy}
|
||||
* and for '<code>release-strategy</code>' elements that include a '<code>method</code>'
|
||||
* attribute (e.g. <release-strategy ref="beanReference" method="methodName"/>).
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class ReleaseStrategyAdapter extends MessageListMethodAdapter implements ReleaseStrategy {
|
||||
|
||||
public ReleaseStrategyAdapter(Object object, Method method) {
|
||||
super(object, method);
|
||||
this.assertMethodReturnsBoolean();
|
||||
}
|
||||
|
||||
public ReleaseStrategyAdapter(Object object, String methodName) {
|
||||
super(object, methodName);
|
||||
this.assertMethodReturnsBoolean();
|
||||
}
|
||||
|
||||
|
||||
public boolean canRelease(MessageGroup messages) {
|
||||
return ((Boolean) executeMethod(messages.getUnmarked())).booleanValue() && messages.getMarked().isEmpty();
|
||||
}
|
||||
|
||||
private void assertMethodReturnsBoolean() {
|
||||
Assert.isTrue(Boolean.class.equals(this.getMethod().getReturnType())
|
||||
|| boolean.class.equals(this.getMethod().getReturnType()),
|
||||
"Method '" + getMethod().getName() + "' does not return a boolean value");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
/**
|
||||
* This class implements all the strategy interfaces needed for a default resequencer.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ResequencingMessageGroupProcessor implements MessageGroupProcessor {
|
||||
|
||||
private volatile Comparator<Message<?>> comparator = new SequenceNumberComparator();
|
||||
|
||||
/**
|
||||
* A comparator to use to order messages before processing. The default is to order by sequence number.
|
||||
*
|
||||
* @param comparator the comparator to use to order messages
|
||||
*/
|
||||
public void setComparator(Comparator<Message<?>> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
public void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate, MessageChannel outputChannel) {
|
||||
Collection<Message<?>> messages = group.getUnmarked();
|
||||
if (messages.size() > 0) {
|
||||
List<Message<?>> sorted = new ArrayList<Message<?>>(messages);
|
||||
Collections.sort(sorted, comparator);
|
||||
for (Message<?> message : sorted) {
|
||||
channelTemplate.send(message, outputChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
public class SequenceNumberComparator implements Comparator<Message<?>> {
|
||||
|
||||
/**
|
||||
* If both messages have a sequence number then compare that, otherwise if one has a sequence number and the other
|
||||
* doesn't then the numbered message comes first, or finally of neither has a sequence number then they are equal in
|
||||
* rank.
|
||||
*/
|
||||
public int compare(Message<?> o1, Message<?> o2) {
|
||||
Integer sequenceNumber1 = o1.getHeaders().getSequenceNumber();
|
||||
Integer sequenceNumber2 = o2.getHeaders().getSequenceNumber();
|
||||
if (sequenceNumber1 == sequenceNumber2) {
|
||||
return 0;
|
||||
}
|
||||
if (sequenceNumber1 == null) {
|
||||
return -sequenceNumber2;
|
||||
}
|
||||
if (sequenceNumber2 == null) {
|
||||
return sequenceNumber1;
|
||||
}
|
||||
return sequenceNumber1.compareTo(sequenceNumber2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.aggregator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ReleaseStrategy} that simply compares the
|
||||
* current size of the message list to the expected 'sequenceSize'.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class SequenceSizeReleaseStrategy implements ReleaseStrategy {
|
||||
|
||||
private volatile Comparator<Message<?>> comparator = new SequenceNumberComparator();
|
||||
|
||||
private volatile boolean releasePartialSequences;
|
||||
|
||||
public SequenceSizeReleaseStrategy() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public SequenceSizeReleaseStrategy(boolean releasePartialSequences) {
|
||||
this.releasePartialSequences = releasePartialSequences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag that determines if partial sequences are allowed. If true then as soon as enough messages arrive that can be
|
||||
* ordered they will be released, provided they all have sequence numbers greater than those already released.
|
||||
*
|
||||
* @param releasePartialSequences
|
||||
*/
|
||||
public void setReleasePartialSequences(boolean releasePartialSequences) {
|
||||
this.releasePartialSequences = releasePartialSequences;
|
||||
}
|
||||
|
||||
public boolean canRelease(MessageGroup messages) {
|
||||
if (releasePartialSequences) {
|
||||
List<Message<?>> sorted = new ArrayList<Message<?>>(messages.getUnmarked());
|
||||
Collections.sort(sorted, comparator);
|
||||
int head = sorted.get(sorted.size() - 1).getHeaders().getSequenceNumber();
|
||||
int tail = sorted.get(0).getHeaders().getSequenceNumber() - 1;
|
||||
return tail == messages.getMarked().size() && head - tail == sorted.size();
|
||||
}
|
||||
return messages.isComplete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.aggregator;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
/**
|
||||
* A {@link ReleaseStrategy} that releases all messages if any of the following is true:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The sequence is complete (if there is one).</li>
|
||||
* <li>There are more messages than a threshold set by the user.</li>
|
||||
* <li>The time elapsed since the earliest message, according to their timestamps, exceeds a timeout set by the user.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class TimeoutCountSequenceSizeReleaseStrategy implements ReleaseStrategy {
|
||||
|
||||
/**
|
||||
* Default timeout is one minute.
|
||||
*/
|
||||
public static final long DEFAULT_TIMEOUT = 60 * 1000;
|
||||
|
||||
/**
|
||||
* Default threshold is effectively infinite.
|
||||
*/
|
||||
public static final int DEFAULT_THRESHOLD = Integer.MAX_VALUE;
|
||||
|
||||
private final int threshold;
|
||||
|
||||
private final long timeout;
|
||||
|
||||
public TimeoutCountSequenceSizeReleaseStrategy() {
|
||||
this(DEFAULT_THRESHOLD, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threshold the number of messages to accept before releasing
|
||||
* @param timeout the timeout for the release in milliseconds
|
||||
*/
|
||||
public TimeoutCountSequenceSizeReleaseStrategy(int threshold, long timeout) {
|
||||
this.threshold = threshold;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public boolean canRelease(MessageGroup messages) {
|
||||
long elapsedTime = System.currentTimeMillis() - findEarliestTimestamp(messages);
|
||||
return messages.isComplete() || messages.getUnmarked().size() >= threshold || elapsedTime > timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param messages the message group
|
||||
* @return the earliest timestamp or Long.MAX_VALUE
|
||||
*/
|
||||
private long findEarliestTimestamp(MessageGroup messages) {
|
||||
long result = Long.MAX_VALUE;
|
||||
for (Message<?> message : messages.getUnmarked()) {
|
||||
long timestamp = message.getHeaders().getTimestamp();
|
||||
if (timestamp < result) {
|
||||
result = timestamp;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.integration.aggregator.CorrelatingMessageHandler;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of aggregating messages.
|
||||
* <p>
|
||||
* A method annotated with @Aggregator may accept a collection
|
||||
* of Messages or Message payloads and should return a single
|
||||
* Message or a single Object to be used as a Message payload.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Aggregator {
|
||||
|
||||
/**
|
||||
* channel name for receiving messages to be aggregated
|
||||
*/
|
||||
String inputChannel() default "";
|
||||
|
||||
/**
|
||||
* channel name for sending aggregated result messages
|
||||
*/
|
||||
String outputChannel() default "";
|
||||
|
||||
/**
|
||||
* channel name for sending discarded messages (due to a timeout)
|
||||
*/
|
||||
String discardChannel() default "";
|
||||
|
||||
/**
|
||||
* timeout for sending results to the reply target (in milliseconds)
|
||||
*/
|
||||
long sendTimeout() default CorrelatingMessageHandler.DEFAULT_SEND_TIMEOUT;
|
||||
|
||||
/**
|
||||
* indicates whether to send an incomplete aggregate on expiry of the message group
|
||||
*/
|
||||
boolean sendPartialResultsOnExpiry() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a given method is capable of determining the correlation key
|
||||
* of a message sent as parameter.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
@Retention (RetentionPolicy.RUNTIME)
|
||||
@Target (ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface CorrelationStrategy {
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of playing the role of a Message Filter.
|
||||
* <p>
|
||||
* A method annotated with @Filter may accept a parameter of type
|
||||
* {@link org.springframework.integration.core.Message} or of the expected
|
||||
* Message payload's type. Any type conversion supported by default or any
|
||||
* Converters registered with the "integrationConversionService" bean will be
|
||||
* applied to the Message payload if necessary. Header values can also be passed
|
||||
* as Message parameters by using the {@link Header @Header} parameter annotation.
|
||||
* <p>
|
||||
* The return type of the annotated method must be a boolean (or Boolean).
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Filter {
|
||||
|
||||
String inputChannel() default "";
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of mapping its parameters to a message
|
||||
* or message payload. These method-level annotations are detected by the
|
||||
* {@link org.springframework.integration.gateway.GatewayProxyFactoryBean}
|
||||
* where the annotation attributes can override the default channel settings.
|
||||
*
|
||||
* <p>A method annotated with @Gateway may accept a single non-annotated
|
||||
* parameter of type {@link org.springframework.integration.core.Message}
|
||||
* or of the intended Message payload type. Method parameters may be mapped
|
||||
* to individual Message header values by using the {@link Header @Header}
|
||||
* parameter annotation. Alternatively, to pass the entire Message headers
|
||||
* map, a Map-typed parameter may be annotated with {@link Headers}.
|
||||
*
|
||||
* <p>Return values from the annotated method may be of any type. If the
|
||||
* declared return value is not a Message, the reply Message's payload will be
|
||||
* returned and any type conversion as supported by Spring's
|
||||
* {@link org.springframework.beans.SimpleTypeConverter} will be applied to
|
||||
* the return value if necessary.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Gateway {
|
||||
|
||||
String requestChannel() default "";
|
||||
|
||||
String replyChannel() default "";
|
||||
|
||||
long requestTimeout() default -1;
|
||||
|
||||
long replyTimeout() default -1;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a method parameter's value should be
|
||||
* retrieved from the message headers. The value of the annotation
|
||||
* provides the header name, and the optional 'required' property
|
||||
* specifies whether the attribute value must be available within
|
||||
* the header. The default value for 'required' is <code>true</code>.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Header {
|
||||
|
||||
String value() default "";
|
||||
|
||||
boolean required() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a method parameter's value should be mapped to or
|
||||
* from the message headers. The annotated parameter must be assignable to
|
||||
* {@link java.util.Map}, and all of the Map's keys must be Strings.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Headers {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Stereotype annotation indicating that a class is capable of serving as a
|
||||
* Message Endpoint.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Component
|
||||
public @interface MessageEndpoint {
|
||||
|
||||
/**
|
||||
* The value may indicate a suggestion for a logical component name,
|
||||
* to be turned into a Spring bean in case of an autodetected component.
|
||||
*
|
||||
* @return the suggested component name, if any
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation allows you to specify a SpEL expression indicating that a method
|
||||
* parameter's value should be mapped from the payload of a Message. The expression
|
||||
* will be evaluated against the payload object as the root context. The annotated
|
||||
* parameter type must match or be convertible from the evaluation result.
|
||||
* <p>
|
||||
* Example: void foo(@Payload("city.name") String cityName) - will map the value of
|
||||
* the 'name' property of the 'city' property of the payload object.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.0
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Payload {
|
||||
|
||||
/**
|
||||
* Expression for matching against nested properties of the payload.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of asserting if a list of messages or
|
||||
* payload objects is complete.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface ReleaseStrategy {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of resolving to a channel or channel name
|
||||
* based on a message, message header(s), or both.
|
||||
* <p>
|
||||
* A method annotated with @Router may accept a parameter of type
|
||||
* {@link org.springframework.integration.core.Message} or of the expected
|
||||
* Message payload's type. Any type conversion supported by
|
||||
* {@link org.springframework.beans.SimpleTypeConverter} will be applied to
|
||||
* the Message payload if necessary. Header values can also be passed as
|
||||
* Message parameters by using the {@link Header @Header} parameter annotation.
|
||||
* <p>
|
||||
* Return values from the annotated method may be either a Collection or Array
|
||||
* whose elements are either
|
||||
* {@link org.springframework.integration.core.MessageChannel channels} or
|
||||
* Strings. In the latter case, the endpoint hosting this router will attempt
|
||||
* to resolve each channel name with the Channel Registry.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Router {
|
||||
|
||||
String inputChannel() default "";
|
||||
|
||||
String defaultOutputChannel() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of handling a message or message payload.
|
||||
* <p>
|
||||
* A method annotated with @ServiceActivator may accept a parameter of type
|
||||
* {@link org.springframework.integration.core.Message} or of the expected
|
||||
* Message payload's type. Any type conversion supported by
|
||||
* {@link org.springframework.beans.SimpleTypeConverter} will be applied to
|
||||
* the Message payload if necessary. Header values can also be passed as
|
||||
* Message parameters by using the {@link Header @Header} parameter annotation.
|
||||
* <p>
|
||||
* Return values from the annotated method may be of any type. If the return
|
||||
* value is not a Message, a reply Message will be created with that object
|
||||
* as its payload.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface ServiceActivator {
|
||||
|
||||
String inputChannel() default "";
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of splitting a single message or message
|
||||
* payload to produce multiple messages or payloads.
|
||||
* <p>
|
||||
* A method annotated with @Splitter may accept a parameter of type
|
||||
* {@link org.springframework.integration.core.Message} or of the expected
|
||||
* Message payload's type. Any type conversion supported by
|
||||
* {@link org.springframework.beans.SimpleTypeConverter} will be applied to
|
||||
* the Message payload if necessary. Header values can also be passed as
|
||||
* Message parameters by using the {@link Header @Header} parameter annotation.
|
||||
* <p>
|
||||
* Return values from the annotated method may be either a Collection or Array
|
||||
* with elements of any type. If the type is not a Message, each will be used
|
||||
* as the payload for creating a new Message.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Splitter {
|
||||
|
||||
String inputChannel() default "";
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
/**
|
||||
* Indicates that a method is capable of transforming a message, message header,
|
||||
* or message payload.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Transformer {
|
||||
|
||||
String inputChannel() default "";
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aop;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
|
||||
/**
|
||||
* Base class for {@link ExpressionSource} implementations.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractExpressionSource implements ExpressionSource {
|
||||
|
||||
private volatile String methodNameVariableName = ExpressionSource.DEFAULT_METHOD_NAME_VARIABLE_NAME;
|
||||
|
||||
private volatile String argumentMapVariableName = ExpressionSource.DEFAULT_ARGUMENT_MAP_VARIABLE_NAME;
|
||||
|
||||
private volatile String returnValueVariableName = ExpressionSource.DEFAULT_RETURN_VALUE_VARIABLE_NAME;
|
||||
|
||||
private volatile String exceptionVariableName = ExpressionSource.DEFAULT_EXCEPTION_VARIABLE_NAME;
|
||||
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
|
||||
public void setMethodNameVariableName(String methodNameVariableName) {
|
||||
this.methodNameVariableName = methodNameVariableName;
|
||||
}
|
||||
|
||||
public String getMethodNameVariableName(Method method) {
|
||||
return this.methodNameVariableName;
|
||||
}
|
||||
|
||||
public void setArgumentMapVariableName(String argumentMapVariableName) {
|
||||
this.argumentMapVariableName = argumentMapVariableName;
|
||||
}
|
||||
|
||||
public String getArgumentMapVariableName(Method method) {
|
||||
return this.argumentMapVariableName;
|
||||
}
|
||||
|
||||
public void setExceptionVariableName(String exceptionVariableName) {
|
||||
this.exceptionVariableName = exceptionVariableName;
|
||||
}
|
||||
|
||||
public String getExceptionVariableName(Method method) {
|
||||
return this.exceptionVariableName;
|
||||
}
|
||||
|
||||
public void setReturnValueVariableName(String returnValueVariableName) {
|
||||
this.returnValueVariableName = returnValueVariableName;
|
||||
}
|
||||
|
||||
public String getReturnValueVariableName(Method method) {
|
||||
return this.returnValueVariableName;
|
||||
}
|
||||
|
||||
protected String[] discoverMethodParameterNames(Method method) {
|
||||
return this.parameterNameDiscoverer.getParameterNames(method);
|
||||
}
|
||||
|
||||
public abstract String getPayloadExpression(Method method);
|
||||
|
||||
public abstract String[] getArgumentVariableNames(Method method);
|
||||
|
||||
public abstract String[] getHeaderExpressions(Method method);
|
||||
|
||||
public abstract String getChannelName(Method method);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.aop;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that provides the variable names to use when constructing the
|
||||
* evaluation context for a MessagePublishingInterceptor.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ExpressionBinding {
|
||||
|
||||
/**
|
||||
* Name of the variable in the context that refers to the method name.
|
||||
* <p>The default is "method".
|
||||
*/
|
||||
String methodNameVariableName() default ExpressionSource.DEFAULT_METHOD_NAME_VARIABLE_NAME;
|
||||
|
||||
/**
|
||||
* Names of the arguments as a comma-separated list. If not provided, the
|
||||
* names will be discovered automatically if enabled by the compiler settings.
|
||||
* These names will be used as the keys in the argument Map.
|
||||
*/
|
||||
String argumentVariableNames() default "";
|
||||
|
||||
/**
|
||||
* Name of the variable in the context that refers to the Map of arguments.
|
||||
* <p>The default is "args".
|
||||
*/
|
||||
String argumentMapVariableName() default ExpressionSource.DEFAULT_ARGUMENT_MAP_VARIABLE_NAME;
|
||||
|
||||
/**
|
||||
* Name of the variable in the context that refers to the return value, if any.
|
||||
* <p>The default is "return".
|
||||
*/
|
||||
String returnValueVariableName() default ExpressionSource.DEFAULT_RETURN_VALUE_VARIABLE_NAME;
|
||||
|
||||
/**
|
||||
* Name of the variable in the context that refers to any exception thrown
|
||||
* by the method invocation that is being intercepted.
|
||||
* <p>The default is "exception".
|
||||
*/
|
||||
String exceptionVariableName() default ExpressionSource.DEFAULT_EXCEPTION_VARIABLE_NAME;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.aop;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Strategy for determining the expression string and evaluation context
|
||||
* variable names from a Method.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ExpressionSource {
|
||||
|
||||
static final String DEFAULT_METHOD_NAME_VARIABLE_NAME = "method";
|
||||
|
||||
static final String DEFAULT_ARGUMENT_MAP_VARIABLE_NAME = "args";
|
||||
|
||||
static final String DEFAULT_RETURN_VALUE_VARIABLE_NAME = "return";
|
||||
|
||||
static final String DEFAULT_EXCEPTION_VARIABLE_NAME = "exception";
|
||||
|
||||
|
||||
/**
|
||||
* Returns the expression string to be evaluated for creating the Message
|
||||
* payload.
|
||||
*/
|
||||
String getPayloadExpression(Method method);
|
||||
|
||||
/**
|
||||
* Returns the array of expression strings to be evaluated for any headers
|
||||
* that should be set on the published Message.
|
||||
*/
|
||||
String[] getHeaderExpressions(Method method);
|
||||
|
||||
/**
|
||||
* Returns the variable name to be associated with the intercepted
|
||||
* method's name.
|
||||
*/
|
||||
String getMethodNameVariableName(Method method);
|
||||
|
||||
/**
|
||||
* Returns the variable names to be associated with the intercepted method
|
||||
* invocation's argument array.
|
||||
*/
|
||||
String[] getArgumentVariableNames(Method method);
|
||||
|
||||
/**
|
||||
* Returns the variable name to use in the evaluation context for the Map
|
||||
* of arguments. The keys in this map will be determined by the result of
|
||||
* the {@link #getArgumentVariableNames(Method)} method.
|
||||
*/
|
||||
String getArgumentMapVariableName(Method method);
|
||||
|
||||
/**
|
||||
* Returns the variable name to use in the evaluation context for any
|
||||
* return value resulting from the method invocation.
|
||||
*/
|
||||
String getReturnValueVariableName(Method method);
|
||||
|
||||
/**
|
||||
* Returns the variable name to use in the evaluation context for any
|
||||
* exception thrown from the method invocation.
|
||||
*/
|
||||
String getExceptionVariableName(Method method);
|
||||
|
||||
/**
|
||||
* Returns the channel name to which Messages should be published
|
||||
* for this particular method invocation.
|
||||
*/
|
||||
String getChannelName(Method method);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aop;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.channel.ChannelResolver;
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.message.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link MethodInterceptor} that publishes Messages to a channel. The
|
||||
* payload of the published Message can be derived from arguments or any return
|
||||
* value or exception resulting from the method invocation. That mapping is the
|
||||
* responsibility of the EL expression provided by the ExpressionSource.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MessagePublishingInterceptor implements MethodInterceptor {
|
||||
|
||||
private final MessageChannelTemplate channelTemplate = new MessageChannelTemplate();
|
||||
|
||||
private volatile ExpressionSource expressionSource;
|
||||
|
||||
private final ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
|
||||
|
||||
private volatile ChannelResolver channelResolver;
|
||||
|
||||
|
||||
public MessagePublishingInterceptor(ExpressionSource expressionSource) {
|
||||
Assert.notNull(expressionSource, "expressionSource must not be null");
|
||||
this.expressionSource = expressionSource;
|
||||
}
|
||||
|
||||
|
||||
public void setExpressionSource(ExpressionSource expressionSource) {
|
||||
Assert.notNull(expressionSource, "expressionSource must not be null");
|
||||
this.expressionSource = expressionSource;
|
||||
}
|
||||
|
||||
public void setDefaultChannel(MessageChannel defaultChannel) {
|
||||
this.channelTemplate.setDefaultChannel(defaultChannel);
|
||||
}
|
||||
|
||||
public void setChannelResolver(ChannelResolver channelResolver) {
|
||||
this.channelResolver = channelResolver;
|
||||
}
|
||||
|
||||
public final Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
Assert.notNull(this.expressionSource, "ExpressionSource is required.");
|
||||
final StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
context.addPropertyAccessor(new MapAccessor());
|
||||
Class<?> targetClass = AopUtils.getTargetClass(invocation.getThis());
|
||||
Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
|
||||
String[] argumentNames = this.expressionSource.getArgumentVariableNames(method);
|
||||
context.setVariable(this.expressionSource.getMethodNameVariableName(method), method.getName());
|
||||
if (invocation.getArguments().length > 0 && argumentNames != null) {
|
||||
int index = 0;
|
||||
Map<String, Object> argumentMap = new HashMap<String, Object>();
|
||||
for (String argumentName : argumentNames) {
|
||||
if (invocation.getArguments().length <= index) {
|
||||
break;
|
||||
}
|
||||
argumentMap.put(argumentName, invocation.getArguments()[index++]);
|
||||
}
|
||||
context.setVariable(this.expressionSource.getArgumentMapVariableName(method), argumentMap);
|
||||
}
|
||||
try {
|
||||
Object returnValue = invocation.proceed();
|
||||
context.setVariable(this.expressionSource.getReturnValueVariableName(method), returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
context.setVariable(this.expressionSource.getExceptionVariableName(method), t);
|
||||
throw t;
|
||||
}
|
||||
finally {
|
||||
publishMessage(method, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void publishMessage(Method method, StandardEvaluationContext context) throws Exception {
|
||||
String payloadExpressionString = this.expressionSource.getPayloadExpression(method);
|
||||
if (!StringUtils.hasText(payloadExpressionString)) {
|
||||
payloadExpressionString = "#" + this.expressionSource.getReturnValueVariableName(method);
|
||||
}
|
||||
Expression expression = this.parser.parseExpression(payloadExpressionString);
|
||||
Object result = expression.getValue(context);
|
||||
if (result != null) {
|
||||
MessageBuilder<?> builder = (result instanceof Message<?>)
|
||||
? MessageBuilder.fromMessage((Message<?>) result)
|
||||
: MessageBuilder.withPayload(result);
|
||||
Map<String, Object> headers = this.evaluateHeaders(method, context);
|
||||
if (headers != null) {
|
||||
builder.copyHeaders(headers);
|
||||
}
|
||||
Message<?> message = builder.build();
|
||||
String channelName = this.expressionSource.getChannelName(method);
|
||||
MessageChannel channel = null;
|
||||
if (channelName != null) {
|
||||
Assert.state(this.channelResolver != null, "ChannelResolver is required to resolve channel names.");
|
||||
channel = this.channelResolver.resolveChannelName(channelName);
|
||||
}
|
||||
if (channel != null) {
|
||||
this.channelTemplate.send(message, channel);
|
||||
}
|
||||
else {
|
||||
this.channelTemplate.send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> evaluateHeaders(Method method, StandardEvaluationContext context)
|
||||
throws ParseException, EvaluationException {
|
||||
|
||||
String[] headerExpressionStrings = this.expressionSource.getHeaderExpressions(method);
|
||||
if (headerExpressionStrings != null) {
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
context.setRootObject(headers);
|
||||
for (String headerExpression : headerExpressionStrings) {
|
||||
if (StringUtils.hasText(headerExpression)) {
|
||||
Expression expression = this.parser.parseExpression(headerExpression);
|
||||
expression.getValue(context);
|
||||
}
|
||||
}
|
||||
if (headers.size() > 0) {
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.aop;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link ExpressionSource} implementation that retrieves the expression
|
||||
* string and evaluation context variable names from an annotation.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MethodAnnotationExpressionSource implements ExpressionSource {
|
||||
|
||||
private final Set<Class<? extends Annotation>> annotationTypes;
|
||||
|
||||
private volatile String channelAttributeName = "channel";
|
||||
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
|
||||
public MethodAnnotationExpressionSource() {
|
||||
this(Collections.<Class<? extends Annotation>>singleton(Publisher.class));
|
||||
}
|
||||
|
||||
public MethodAnnotationExpressionSource(Set<Class<? extends Annotation>> annotationTypes) {
|
||||
Assert.notEmpty(annotationTypes, "annotationTypes must not be empty");
|
||||
this.annotationTypes = annotationTypes;
|
||||
}
|
||||
|
||||
|
||||
public void setChannelAttributeName(String channelAttributeName) {
|
||||
Assert.hasText(channelAttributeName, "channelAttributeName must not be empty");
|
||||
this.channelAttributeName = channelAttributeName;
|
||||
}
|
||||
|
||||
public String getPayloadExpression(Method method) {
|
||||
return this.getAnnotationValue(method, null, String.class);
|
||||
}
|
||||
|
||||
public String[] getHeaderExpressions(Method method) {
|
||||
return this.getAnnotationValue(method, "headers", String[].class);
|
||||
}
|
||||
|
||||
public String getMethodNameVariableName(Method method) {
|
||||
ExpressionBinding annotation = AnnotationUtils.findAnnotation(method, ExpressionBinding.class);
|
||||
if (annotation != null) {
|
||||
return annotation.methodNameVariableName();
|
||||
}
|
||||
return ExpressionSource.DEFAULT_METHOD_NAME_VARIABLE_NAME;
|
||||
}
|
||||
|
||||
public String[] getArgumentVariableNames(Method method) {
|
||||
ExpressionBinding annotation = AnnotationUtils.findAnnotation(method, ExpressionBinding.class);
|
||||
if (annotation != null) {
|
||||
String argNameList = annotation.argumentVariableNames();
|
||||
if (StringUtils.hasText(argNameList)) {
|
||||
return StringUtils.tokenizeToStringArray(argNameList, ",");
|
||||
}
|
||||
}
|
||||
return this.parameterNameDiscoverer.getParameterNames(method);
|
||||
}
|
||||
|
||||
public String getArgumentMapVariableName(Method method) {
|
||||
ExpressionBinding annotation = AnnotationUtils.findAnnotation(method, ExpressionBinding.class);
|
||||
if (annotation != null) {
|
||||
return annotation.argumentMapVariableName();
|
||||
}
|
||||
return ExpressionSource.DEFAULT_ARGUMENT_MAP_VARIABLE_NAME;
|
||||
}
|
||||
|
||||
public String getReturnValueVariableName(Method method) {
|
||||
ExpressionBinding annotation = AnnotationUtils.findAnnotation(method, ExpressionBinding.class);
|
||||
if (annotation != null) {
|
||||
return annotation.returnValueVariableName();
|
||||
}
|
||||
return ExpressionSource.DEFAULT_RETURN_VALUE_VARIABLE_NAME;
|
||||
}
|
||||
|
||||
public String getExceptionVariableName(Method method) {
|
||||
ExpressionBinding annotation = AnnotationUtils.findAnnotation(method, ExpressionBinding.class);
|
||||
if (annotation != null) {
|
||||
return annotation.exceptionVariableName();
|
||||
}
|
||||
return ExpressionSource.DEFAULT_EXCEPTION_VARIABLE_NAME;
|
||||
}
|
||||
|
||||
public String getChannelName(Method method) {
|
||||
String channelName = this.getAnnotationValue(method, this.channelAttributeName, String.class);
|
||||
return (StringUtils.hasText(channelName) ? channelName : null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getAnnotationValue(Method method, String attributeName, Class<T> expectedType) {
|
||||
T value = null;
|
||||
for (Class<? extends Annotation> annotationType : this.annotationTypes) {
|
||||
Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType);
|
||||
if (annotation != null) {
|
||||
if (value != null) {
|
||||
throw new IllegalStateException(
|
||||
"method [" + method + "] contains more than one publisher annotation");
|
||||
}
|
||||
Object valueAsObject = (attributeName == null) ? AnnotationUtils.getValue(annotation)
|
||||
: AnnotationUtils.getValue(annotation, attributeName);
|
||||
if (valueAsObject != null) {
|
||||
if (expectedType.isAssignableFrom(valueAsObject.getClass())) {
|
||||
value = (T) valueAsObject;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("expected type [" + expectedType.getName() +
|
||||
"] for attribute '" + attributeName + "' on publisher annotation [" +
|
||||
annotationType + "], but actual type was [" + valueAsObject.getClass() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aop;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MethodNameMappingExpressionSource extends AbstractExpressionSource {
|
||||
|
||||
private final Map<String, String> payloadExpressionMap;
|
||||
|
||||
private volatile Map<String, String[]> headerExpressionMap = Collections.emptyMap();
|
||||
|
||||
private volatile Map<String, String> channelMap = Collections.emptyMap();
|
||||
|
||||
private volatile Map<String, String[]> argumentVariableNameMap;
|
||||
|
||||
|
||||
public MethodNameMappingExpressionSource(Map<String, String> payloadExpressionMap) {
|
||||
Assert.notEmpty(payloadExpressionMap, "payloadExpressionMap must not be empty");
|
||||
this.payloadExpressionMap = payloadExpressionMap;
|
||||
}
|
||||
|
||||
public void setArgumentVariableNameMap(Map<String, String[]> argumentVariableNameMap) {
|
||||
this.argumentVariableNameMap = argumentVariableNameMap;
|
||||
}
|
||||
|
||||
public void setHeaderExpressionMap(Map<String, String[]> headerExpressionMap) {
|
||||
this.headerExpressionMap = headerExpressionMap;
|
||||
}
|
||||
|
||||
public void setChannelMap(Map<String, String> channelMap) {
|
||||
this.channelMap = channelMap;
|
||||
}
|
||||
|
||||
public String[] getArgumentVariableNames(Method method) {
|
||||
if (this.argumentVariableNameMap != null) {
|
||||
for (Map.Entry<String, String[]> entry : this.argumentVariableNameMap.entrySet()) {
|
||||
if (PatternMatchUtils.simpleMatch(entry.getKey(), method.getName())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.discoverMethodParameterNames(method);
|
||||
}
|
||||
|
||||
public String getPayloadExpression(Method method) {
|
||||
for (Map.Entry<String, String> entry : this.payloadExpressionMap.entrySet()) {
|
||||
if (PatternMatchUtils.simpleMatch(entry.getKey(), method.getName())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String[] getHeaderExpressions(Method method) {
|
||||
for (Map.Entry<String, String[]> entry : this.headerExpressionMap.entrySet()) {
|
||||
if (PatternMatchUtils.simpleMatch(entry.getKey(), method.getName())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getChannelName(Method method) {
|
||||
for (Map.Entry<String, String> entry : this.channelMap.entrySet()) {
|
||||
if (PatternMatchUtils.simpleMatch(entry.getKey(), method.getName())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.aop;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to indicate that a method, or all public methods if applied at
|
||||
* class-level, should publish Messages whose payloads will be determined by
|
||||
* the provided EL expression.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Publisher {
|
||||
|
||||
/**
|
||||
* String representation of a Spel Expression to evaluate when creating the
|
||||
* Message payload. The default will be empty, thereby causing the return
|
||||
* value to be used as the payload.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* String representations of Spel Expressions to evaluate for adding any
|
||||
* headers to the Message. Optional.
|
||||
*/
|
||||
String[] headers() default "";
|
||||
|
||||
/**
|
||||
* Name of the Message Channel to which Messages will be published.
|
||||
*/
|
||||
String channel() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.aop;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.integration.channel.BeanFactoryChannelResolver;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
|
||||
/**
|
||||
* An advisor that will apply the {@link MessagePublishingInterceptor} to any
|
||||
* methods containing the provided annotations. If no annotations are provided,
|
||||
* the default will be {@link Publisher @Publisher}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class PublisherAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||
|
||||
private final Set<Class<? extends Annotation>> publisherAnnotationTypes;
|
||||
|
||||
private final MessagePublishingInterceptor interceptor;
|
||||
|
||||
|
||||
public PublisherAnnotationAdvisor(Class<? extends Annotation> ... publisherAnnotationTypes) {
|
||||
this.publisherAnnotationTypes = new HashSet<Class<? extends Annotation>>(Arrays.asList(publisherAnnotationTypes));
|
||||
ExpressionSource source = new MethodAnnotationExpressionSource(this.publisherAnnotationTypes);
|
||||
this.interceptor = new MessagePublishingInterceptor(source);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PublisherAnnotationAdvisor() {
|
||||
this(Publisher.class);
|
||||
}
|
||||
|
||||
|
||||
public void setDefaultChannel(MessageChannel defaultChannel) {
|
||||
this.interceptor.setDefaultChannel(defaultChannel);
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.interceptor.setChannelResolver(new BeanFactoryChannelResolver(beanFactory));
|
||||
}
|
||||
|
||||
public Advice getAdvice() {
|
||||
return this.interceptor;
|
||||
}
|
||||
|
||||
public Pointcut getPointcut() {
|
||||
return this.buildPointcut();
|
||||
}
|
||||
|
||||
private Pointcut buildPointcut() {
|
||||
ComposablePointcut result = null;
|
||||
for (Class<? extends Annotation> publisherAnnotationType : this.publisherAnnotationTypes) {
|
||||
Pointcut cpc = new AnnotationMatchingPointcut(publisherAnnotationType, true);
|
||||
Pointcut mpc = new AnnotationMatchingPointcut(null, publisherAnnotationType);
|
||||
if (result == null) {
|
||||
result = new ComposablePointcut(cpc).union(mpc);
|
||||
}
|
||||
else {
|
||||
result.union(cpc).union(mpc);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.aop;
|
||||
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyConfig;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Post-processes beans that contain the method-level @{@link Publisher} annotation.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PublisherAnnotationBeanPostProcessor extends ProxyConfig
|
||||
implements BeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, Ordered {
|
||||
|
||||
private volatile MessageChannel defaultChannel;
|
||||
|
||||
private volatile PublisherAnnotationAdvisor advisor;
|
||||
|
||||
private volatile int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private volatile BeanFactory beanFactory;
|
||||
|
||||
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
|
||||
/**
|
||||
* Set the default channel where Messages should be sent if the annotation
|
||||
* itself does not provide a channel.
|
||||
*/
|
||||
public void setDefaultChannel(MessageChannel defaultChannel){
|
||||
this.defaultChannel = defaultChannel;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet(){
|
||||
advisor = new PublisherAnnotationAdvisor();
|
||||
advisor.setBeanFactory(beanFactory);
|
||||
advisor.setDefaultChannel(defaultChannel);
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
if (targetClass == null) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
if (AopUtils.canApply(this.advisor, targetClass)) {
|
||||
if (bean instanceof Advised) {
|
||||
((Advised) bean).addAdvisor(this.advisor);
|
||||
return bean;
|
||||
}
|
||||
else {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(bean);
|
||||
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
|
||||
proxyFactory.copyFrom(this);
|
||||
proxyFactory.addAdvisor(this.advisor);
|
||||
return proxyFactory.getProxy(this.beanClassLoader);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cannot apply advisor
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aop;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ExpressionSource} that allows for
|
||||
* configuration of a single channel name, payload expression, and
|
||||
* array of header key=value expressions.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SimpleExpressionSource extends AbstractExpressionSource {
|
||||
|
||||
private volatile String channelName;
|
||||
|
||||
private volatile String payloadExpression;
|
||||
|
||||
private volatile String[] headerExpressions;
|
||||
|
||||
|
||||
public void setChannelName(String channelName) {
|
||||
this.channelName = channelName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelName(Method method) {
|
||||
return this.channelName;
|
||||
}
|
||||
|
||||
public void setPayloadExpression(String payloadExpression) {
|
||||
this.payloadExpression = payloadExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPayloadExpression(Method method) {
|
||||
return this.payloadExpression;
|
||||
}
|
||||
|
||||
public void setHeaderExpressions(String[] headerExpressions) {
|
||||
this.headerExpressions = headerExpressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getHeaderExpressions(Method method) {
|
||||
return this.headerExpressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getArgumentVariableNames(Method method) {
|
||||
return this.discoverMethodParameterNames(method);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.core.MessagingException;
|
||||
import org.springframework.integration.message.MessageBuilder;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for {@link MessageChannel} implementations providing common
|
||||
* properties such as the channel name. Also provides the common functionality
|
||||
* for sending and receiving {@link Message Messages} including the invocation
|
||||
* of any {@link ChannelInterceptor ChannelInterceptors}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractMessageChannel extends IntegrationObjectSupport implements MessageChannel {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final AtomicLong sendSuccessCount = new AtomicLong();
|
||||
|
||||
private final AtomicLong sendErrorCount = new AtomicLong();
|
||||
|
||||
private volatile Class<?>[] datatypes = new Class<?>[] { Object.class };
|
||||
|
||||
private final ChannelInterceptorList interceptors = new ChannelInterceptorList();
|
||||
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "channel";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current count of Messages that have been sent
|
||||
* to this channel successfully.
|
||||
*/
|
||||
public long getSendSuccessCount() {
|
||||
return this.sendSuccessCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current count of errors that have occurred while
|
||||
* attempting to send a Message to this channel. This value is
|
||||
* incremented whenever an Exception is thrown from one of the
|
||||
* send() methods.
|
||||
*/
|
||||
public long getSendErrorCount() {
|
||||
return this.sendErrorCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the Message payload datatype(s) supported by this channel. If a
|
||||
* payload type does not match directly, but the 'conversionService' is
|
||||
* available, then type conversion will be attempted in the order of the
|
||||
* elements provided in this array.
|
||||
* <p>
|
||||
* If this property is not set explicitly, any Message payload type will be
|
||||
* accepted.
|
||||
* @see #setConversionService(ConversionService)
|
||||
*/
|
||||
public void setDatatypes(Class<?>... datatypes) {
|
||||
this.datatypes = (datatypes != null && datatypes.length > 0)
|
||||
? datatypes : new Class<?>[] { Object.class };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of channel interceptors. This will clear any existing
|
||||
* interceptors.
|
||||
*/
|
||||
public void setInterceptors(List<ChannelInterceptor> interceptors) {
|
||||
Collections.sort(interceptors, new OrderComparator());
|
||||
this.interceptors.set(interceptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a channel interceptor to the end of the list.
|
||||
*/
|
||||
public void addInterceptor(ChannelInterceptor interceptor) {
|
||||
this.interceptors.add(interceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link ConversionService} to use when trying to convert to
|
||||
* one of this channel's supported datatypes for a Message whose payload
|
||||
* does not already match. If this property is not set explicitly but
|
||||
* the channel is managed within a context, it will attempt to locate a
|
||||
* bean named "integrationConversionService" defined within that context.
|
||||
* Finally, if that bean is not available, it will fallback to the
|
||||
* "conversionService" bean, if available.
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
super.setConversionService(conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the interceptor list for subclasses.
|
||||
*/
|
||||
protected ChannelInterceptorList getInterceptors() {
|
||||
return this.interceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message on this channel. If the channel is at capacity, this
|
||||
* method will block until either space becomes available or the sending
|
||||
* thread is interrupted.
|
||||
*
|
||||
* @param message the Message to send
|
||||
*
|
||||
* @return <code>true</code> if the message is sent successfully or
|
||||
* <code>false</code> if the sending thread is interrupted.
|
||||
*/
|
||||
public final boolean send(Message<?> message) {
|
||||
return this.send(message, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message on this channel. If the channel is at capacity, this
|
||||
* method will block until either the timeout occurs or the sending thread
|
||||
* is interrupted. If the specified timeout is 0, the method will return
|
||||
* immediately. If less than zero, it will block indefinitely (see
|
||||
* {@link #send(Message)}).
|
||||
*
|
||||
* @param message the Message to send
|
||||
* @param timeout the timeout in milliseconds
|
||||
*
|
||||
* @return <code>true</code> if the message is sent successfully,
|
||||
* <code>false</code> if the message cannot be sent within the allotted
|
||||
* time or the sending thread is interrupted.
|
||||
*/
|
||||
public final boolean send(Message<?> message, long timeout) {
|
||||
Assert.notNull(message, "message must not be null");
|
||||
Assert.notNull(message.getPayload(), "message payload must not be null");
|
||||
message = this.convertPayloadIfNecessary(message);
|
||||
message.getHeaders().getHistory().addEvent(this);
|
||||
message = this.interceptors.preSend(message, this);
|
||||
if (message == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
boolean sent = this.doSend(message, timeout);
|
||||
if (sent) {
|
||||
this.sendSuccessCount.incrementAndGet();
|
||||
}
|
||||
this.interceptors.postSend(message, this, sent);
|
||||
return sent;
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.sendErrorCount.incrementAndGet();
|
||||
if (e instanceof MessagingException) {
|
||||
throw (MessagingException) e;
|
||||
}
|
||||
throw new MessageDeliveryException(message,
|
||||
"failed to send Message to channel '" + this.getComponentName() + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Message<?> convertPayloadIfNecessary(Message<?> message) {
|
||||
// first pass checks if the payload type already matches any of the datatypes
|
||||
for (Class<?> datatype : this.datatypes) {
|
||||
if (datatype.isAssignableFrom(message.getPayload().getClass())) {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
// second pass applies conversion if possible, attempting datatypes in order
|
||||
ConversionService conversionService = this.getConversionService();
|
||||
if (conversionService != null) {
|
||||
for (Class<?> datatype : this.datatypes) {
|
||||
if (conversionService.canConvert(message.getPayload().getClass(), datatype)) {
|
||||
Object convertedPayload = conversionService.convert(message.getPayload(), datatype);
|
||||
return MessageBuilder.withPayload(convertedPayload).copyHeaders(message.getHeaders()).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new MessageDeliveryException(message, "Channel '" + this.getComponentName() +
|
||||
"' expected one of the following datataypes [" +
|
||||
StringUtils.arrayToCommaDelimitedString(this.datatypes) +
|
||||
"], but received [" + message.getPayload().getClass() + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method. A non-negative timeout indicates
|
||||
* how long to wait if the channel is at capacity (if the value is 0, it
|
||||
* must return immediately with or without success). A negative timeout
|
||||
* value indicates that the method should block until either the message is
|
||||
* accepted or the blocking thread is interrupted.
|
||||
*/
|
||||
protected abstract boolean doSend(Message<?> message, long timeout);
|
||||
|
||||
|
||||
/**
|
||||
* A convenience wrapper class for the list of ChannelInterceptors.
|
||||
*/
|
||||
protected class ChannelInterceptorList {
|
||||
|
||||
private final List<ChannelInterceptor> interceptors = new CopyOnWriteArrayList<ChannelInterceptor>();
|
||||
|
||||
|
||||
public boolean set(List<ChannelInterceptor> interceptors) {
|
||||
synchronized (this.interceptors) {
|
||||
this.interceptors.clear();
|
||||
return this.interceptors.addAll(interceptors);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(ChannelInterceptor interceptor) {
|
||||
return this.interceptors.add(interceptor);
|
||||
}
|
||||
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("preSend on channel '" + channel + "', message: " + message);
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
message = interceptor.preSend(message, channel);
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("postSend (sent=" + sent + ") on channel '" + channel + "', message: " + message);
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
interceptor.postSend(message, channel, sent);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean preReceive(MessageChannel channel) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("preReceive on channel '" + channel + "'");
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
if (!interceptor.preReceive(channel)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
|
||||
if (message != null && logger.isDebugEnabled()) {
|
||||
logger.debug("postReceive on channel '" + channel + "', message: " + message);
|
||||
}
|
||||
else if (logger.isTraceEnabled()) {
|
||||
logger.trace("postReceive on channel '" + channel + "', message is null");
|
||||
}
|
||||
for (ChannelInterceptor interceptor : interceptors) {
|
||||
message = interceptor.postReceive(message, channel);
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* Base class for all pollable channels.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractPollableChannel extends AbstractMessageChannel implements PollableChannel {
|
||||
|
||||
/**
|
||||
* Receive the first available message from this channel. If the channel
|
||||
* contains no messages, this method will block.
|
||||
*
|
||||
* @return the first available message or <code>null</code> if the
|
||||
* receiving thread is interrupted.
|
||||
*/
|
||||
public final Message<?> receive() {
|
||||
return this.receive(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the first available message from this channel. If the channel
|
||||
* contains no messages, this method will block until the allotted timeout
|
||||
* elapses. If the specified timeout is 0, the method will return
|
||||
* immediately. If less than zero, it will block indefinitely (see
|
||||
* {@link #receive()}).
|
||||
*
|
||||
* @param timeout the timeout in milliseconds
|
||||
*
|
||||
* @return the first available message or <code>null</code> if no message
|
||||
* is available within the allotted time or the receiving thread is
|
||||
* interrupted.
|
||||
*/
|
||||
public final Message<?> receive(long timeout) {
|
||||
if (!this.getInterceptors().preReceive(this)) {
|
||||
return null;
|
||||
}
|
||||
Message<?> message = this.doReceive(timeout);
|
||||
message = this.getInterceptors().postReceive(message, this);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method. A non-negative timeout indicates
|
||||
* how long to wait if the channel is empty (if the value is 0, it must
|
||||
* return immediately with or without success). A negative timeout value
|
||||
* indicates that the method should block until either a message is
|
||||
* available or the blocking thread is interrupted.
|
||||
*/
|
||||
protected abstract Message<?> doReceive(long timeout);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.channel;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.dispatcher.MessageDispatcher;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link MessageChannel} that invokes the subscribed
|
||||
* {@link MessageHandler handler(s)} by delegating to a {@link MessageDispatcher}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractSubscribableChannel extends AbstractMessageChannel implements SubscribableChannel {
|
||||
|
||||
public boolean subscribe(MessageHandler handler) {
|
||||
return this.getRequiredDispatcher().addHandler(handler);
|
||||
}
|
||||
|
||||
public boolean unsubscribe(MessageHandler handle) {
|
||||
return this.getRequiredDispatcher().removeHandler(handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
return this.getRequiredDispatcher().dispatch(message);
|
||||
}
|
||||
|
||||
private MessageDispatcher getRequiredDispatcher() {
|
||||
MessageDispatcher dispatcher = this.getDispatcher();
|
||||
Assert.state(dispatcher != null, "'dispatcher' must not be null");
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
protected abstract MessageDispatcher getDispatcher();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ChannelResolver} implementation based on a Spring {@link BeanFactory}.
|
||||
*
|
||||
* <p>Will lookup Spring managed beans identified by bean name,
|
||||
* expecting them to be of type {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
*/
|
||||
public class BeanFactoryChannelResolver implements ChannelResolver, BeanFactoryAware {
|
||||
|
||||
private volatile BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link BeanFactoryChannelResolver} class.
|
||||
* <p>The BeanFactory to access must be set via <code>setBeanFactory</code>.
|
||||
* This will happen automatically if this resolver is defined within an
|
||||
* ApplicationContext thereby receiving the callback upon initialization.
|
||||
* @see #setBeanFactory
|
||||
*/
|
||||
public BeanFactoryChannelResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link BeanFactoryChannelResolver} class.
|
||||
* <p>Use of this constructor is redundant if this object is being created
|
||||
* by a Spring IoC container as the supplied {@link BeanFactory} will be
|
||||
* replaced by the {@link BeanFactory} that creates it (c.f. the
|
||||
* {@link BeanFactoryAware} contract). So only use this constructor if you
|
||||
* are instantiating this object explicitly rather than defining a bean.
|
||||
*
|
||||
* @param beanFactory the bean factory to be used to lookup {@link MessageChannel}s.
|
||||
*/
|
||||
public BeanFactoryChannelResolver(BeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public MessageChannel resolveChannelName(String name) {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory is required");
|
||||
try {
|
||||
return (MessageChannel) this.beanFactory.getBean(name, MessageChannel.class);
|
||||
}
|
||||
catch (BeansException e) {
|
||||
throw new ChannelResolutionException(
|
||||
"failed to look up MessageChannel bean with name '" + name + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
|
||||
/**
|
||||
* Interface for interceptors that are able to view and/or modify the
|
||||
* {@link Message Messages} being sent-to and/or received-from a
|
||||
* {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface ChannelInterceptor {
|
||||
|
||||
/**
|
||||
* Invoked before the Message is actually sent to the channel.
|
||||
* This allows for modification of the Message if necessary.
|
||||
* If this method returns <code>null</code>, then the actual
|
||||
* send invocation will not occur.
|
||||
*/
|
||||
Message<?> preSend(Message<?> message, MessageChannel channel);
|
||||
|
||||
/**
|
||||
* Invoked immediately after the send invocation. The boolean
|
||||
* value argument represents the return value of that invocation.
|
||||
*/
|
||||
void postSend(Message<?> message, MessageChannel channel, boolean sent);
|
||||
|
||||
/**
|
||||
* Invoked as soon as receive is called and before a Message is
|
||||
* actually retrieved. If the return value is 'false', then no
|
||||
* Message will be retrieved. This only applies to PollableChannels.
|
||||
*/
|
||||
boolean preReceive(MessageChannel channel);
|
||||
|
||||
/**
|
||||
* Invoked immediately after a Message has been retrieved but before
|
||||
* it is returned to the caller. The Message may be modified if
|
||||
* necessary. This only applies to PollableChannels.
|
||||
*/
|
||||
Message<?> postReceive(Message<?> message, MessageChannel channel);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.selector.MessageSelector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A utility class for purging {@link Message Messages} from one or more
|
||||
* {@link QueueChannel QueueChannels}. Any message that does <em>not</em>
|
||||
* match the provided {@link MessageSelector} will be removed from the channel.
|
||||
* If no {@link MessageSelector} is provided, then <em>all</em> messages will be
|
||||
* cleared from the channel.
|
||||
* <p>
|
||||
* Note that the {@link #purge()} method operates on a snapshot of the messages
|
||||
* within a channel at the time that the method is invoked. It is therefore
|
||||
* possible that new messages will arrive on the channel during the purge
|
||||
* operation and thus will <em>not</em> be removed. Likewise, messages to be
|
||||
* purged may have been removed from the channel while the operation is taking
|
||||
* place. Such messages will not be included in the returned list.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ChannelPurger {
|
||||
|
||||
private final QueueChannel[] channels;
|
||||
|
||||
private final MessageSelector selector;
|
||||
|
||||
|
||||
public ChannelPurger(QueueChannel ... channels) {
|
||||
this(null, channels);
|
||||
}
|
||||
|
||||
public ChannelPurger(MessageSelector selector, QueueChannel ... channels) {
|
||||
Assert.notEmpty(channels, "at least one channel is required");
|
||||
if (channels.length == 1) {
|
||||
Assert.notNull(channels[0], "channel must not be null");
|
||||
}
|
||||
this.selector = selector;
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
|
||||
public final List<Message<?>> purge() {
|
||||
List<Message<?>> purgedMessages = new ArrayList<Message<?>>();
|
||||
for (QueueChannel channel : this.channels) {
|
||||
List<Message<?>> results = (this.selector == null) ?
|
||||
channel.clear() : channel.purge(this.selector);
|
||||
if (results != null) {
|
||||
purgedMessages.addAll(results);
|
||||
}
|
||||
}
|
||||
return purgedMessages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel;
|
||||
|
||||
import org.springframework.integration.core.MessagingException;
|
||||
|
||||
/**
|
||||
* Thrown by a ChannelResolver when it cannot resolve a channel name.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @see ChannelResolver
|
||||
*/
|
||||
public class ChannelResolutionException extends MessagingException {
|
||||
|
||||
/**
|
||||
* Create a new ChannelResolutionException.
|
||||
* @param description the description
|
||||
*/
|
||||
public ChannelResolutionException(String description) {
|
||||
super(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ChannelResolutionException.
|
||||
* @param description the description
|
||||
* @param cause the root cause (if any)
|
||||
*/
|
||||
public ChannelResolutionException(String description, Throwable cause) {
|
||||
super(description, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel;
|
||||
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
|
||||
|
||||
/**
|
||||
* Strategy for resolving a name to a {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface ChannelResolver {
|
||||
|
||||
/**
|
||||
* Return the MessageChannel for the given name.
|
||||
*/
|
||||
MessageChannel resolveChannelName(String channelName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.channel;
|
||||
|
||||
import org.springframework.integration.dispatcher.LoadBalancingStrategy;
|
||||
import org.springframework.integration.dispatcher.UnicastingDispatcher;
|
||||
|
||||
/**
|
||||
* A channel that invokes a single subscriber for each sent Message.
|
||||
* The invocation will occur in the sender's thread.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class DirectChannel extends AbstractSubscribableChannel {
|
||||
|
||||
private final UnicastingDispatcher dispatcher = new UnicastingDispatcher();
|
||||
|
||||
|
||||
/**
|
||||
* Create a channel with no {@link LoadBalancingStrategy}.
|
||||
* The dispatcher for such a channel will invoke its
|
||||
* MessageHandlers in a fixed-order.
|
||||
*/
|
||||
public DirectChannel() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DirectChannel with a {@link LoadBalancingStrategy}. The
|
||||
* strategy <emphasis>must not</emphasis> be null.
|
||||
*/
|
||||
public DirectChannel(LoadBalancingStrategy loadBalancingStrategy) {
|
||||
this.dispatcher.setLoadBalancingStrategy(loadBalancingStrategy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify whether the channel's dispatcher should have failover enabled.
|
||||
* By default, it will. Set this value to 'false' to disable it.
|
||||
*/
|
||||
public void setFailover(boolean failover) {
|
||||
this.dispatcher.setFailover(failover);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UnicastingDispatcher getDispatcher() {
|
||||
return this.dispatcher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.dispatcher.LoadBalancingStrategy;
|
||||
import org.springframework.integration.dispatcher.UnicastingDispatcher;
|
||||
import org.springframework.integration.util.ErrorHandlingTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
* An implementation of {@link MessageChannel} that delegates to an instance of
|
||||
* {@link UnicastingDispatcher} which in turn delegates all dispatching
|
||||
* invocations to an {@link Executor}.
|
||||
* <p>
|
||||
* <emphasis>NOTE: unlike DirectChannel, the ExecutorChannel does not support a
|
||||
* shared transactional context between sender and handler, because the
|
||||
* {@link Executor} typically does not block the sender's Thread since it
|
||||
* uses another Thread for the dispatch.</emphasis> (SyncTaskExecutor is an
|
||||
* exception but would provide no value for this channel. If synchronous
|
||||
* dispatching is required, a DirectChannel should be used instead).
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 1.0.3
|
||||
*/
|
||||
public class ExecutorChannel extends AbstractSubscribableChannel {
|
||||
|
||||
private volatile UnicastingDispatcher dispatcher;
|
||||
|
||||
private volatile Executor executor;
|
||||
|
||||
private volatile boolean failover = true;
|
||||
|
||||
private volatile LoadBalancingStrategy loadBalancingStrategy;
|
||||
|
||||
|
||||
/**
|
||||
* Create an ExecutorChannel that delegates to the provided
|
||||
* {@link Executor} when dispatching Messages.
|
||||
* <p>
|
||||
* The Executor must not be null.
|
||||
*/
|
||||
public ExecutorChannel(Executor executor) {
|
||||
this(executor, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ExecutorChannel with a {@link LoadBalancingStrategy} that
|
||||
* delegates to the provided {@link Executor} when dispatching Messages.
|
||||
* <p>
|
||||
* The Executor must not be null.
|
||||
*/
|
||||
public ExecutorChannel(Executor executor, LoadBalancingStrategy loadBalancingStrategy) {
|
||||
Assert.notNull(executor, "executor must not be null");
|
||||
this.executor = executor;
|
||||
this.dispatcher = new UnicastingDispatcher(executor);
|
||||
if (loadBalancingStrategy != null) {
|
||||
this.loadBalancingStrategy = loadBalancingStrategy;
|
||||
this.dispatcher.setLoadBalancingStrategy(loadBalancingStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify whether the channel's dispatcher should have failover enabled.
|
||||
* By default, it will. Set this value to 'false' to disable it.
|
||||
*/
|
||||
public void setFailover(boolean failover) {
|
||||
this.failover = failover;
|
||||
this.dispatcher.setFailover(failover);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UnicastingDispatcher getDispatcher() {
|
||||
return this.dispatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onInit() {
|
||||
if (!(this.executor instanceof ErrorHandlingTaskExecutor)) {
|
||||
ErrorHandler errorHandler = new MessagePublishingErrorHandler(
|
||||
new BeanFactoryChannelResolver(this.getBeanFactory()));
|
||||
this.executor = new ErrorHandlingTaskExecutor(this.executor, errorHandler);
|
||||
}
|
||||
this.dispatcher = new UnicastingDispatcher(this.executor);
|
||||
this.dispatcher.setFailover(this.failover);
|
||||
if (this.loadBalancingStrategy != null) {
|
||||
this.dispatcher.setLoadBalancingStrategy(this.loadBalancingStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.channel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ChannelResolver} implementation that resolves {@link MessageChannel}
|
||||
* instances by matching the channel name against keys within a Map.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MapBasedChannelResolver implements ChannelResolver {
|
||||
|
||||
private volatile Map<String, ? extends MessageChannel> channelMap = new HashMap<String, MessageChannel>();
|
||||
|
||||
/**
|
||||
* Empty constructor for use when providing the channel map via
|
||||
* {@link #setChannelMap(Map)}.
|
||||
*/
|
||||
public MapBasedChannelResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ChannelResolver} that uses the provided Map.
|
||||
* Each String key will resolve to the associated channel value.
|
||||
*/
|
||||
public MapBasedChannelResolver(Map<String, ? extends MessageChannel> channelMap) {
|
||||
this.setChannelMap(channelMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a map of channels to be used by this resolver.
|
||||
* Each String key will resolve to the associated channel value.
|
||||
*/
|
||||
public void setChannelMap(Map<String, ? extends MessageChannel> channelMap) {
|
||||
Assert.notNull(channelMap, "channelMap must not be null");
|
||||
this.channelMap = channelMap;
|
||||
}
|
||||
|
||||
public MessageChannel resolveChannelName(String channelName) {
|
||||
return this.channelMap.get(channelName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.channel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.core.MessageHeaders;
|
||||
import org.springframework.integration.message.MessageBuilder;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.selector.MessageSelector;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This is the central class for invoking message exchange operations across
|
||||
* {@link MessageChannel}s. It supports one-way send and receive calls as well
|
||||
* as request/reply.
|
||||
* <p>
|
||||
* To enable transactions, configure the 'transactionManager' property with a
|
||||
* reference to an instance of Spring's {@link PlatformTransactionManager}
|
||||
* strategy and optionally provide the other transactional attributes
|
||||
* (e.g. 'propagationBehaviorName').
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MessageChannelTemplate implements InitializingBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile MessageChannel defaultChannel;
|
||||
|
||||
private volatile long sendTimeout = -1;
|
||||
|
||||
private volatile long receiveTimeout = -1;
|
||||
|
||||
private volatile PlatformTransactionManager transactionManager;
|
||||
|
||||
private volatile TransactionTemplate transactionTemplate;
|
||||
|
||||
private volatile String propagationBehaviorName = "PROPAGATION_REQUIRED";
|
||||
|
||||
private volatile String isolationLevelName = "ISOLATION_DEFAULT";
|
||||
|
||||
private volatile int transactionTimeout = -1;
|
||||
|
||||
private volatile boolean readOnly = false;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Create a MessageChannelTemplate with no default channel. Note, that one
|
||||
* may be provided by invoking {@link #setDefaultChannel(MessageChannel)}.
|
||||
*/
|
||||
public MessageChannelTemplate() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MessageChannelTemplate with the given default channel.
|
||||
*/
|
||||
public MessageChannelTemplate(MessageChannel defaultChannel) {
|
||||
this.defaultChannel = defaultChannel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the default MessageChannel to use when invoking the send and/or
|
||||
* receive methods that do not expect a channel parameter.
|
||||
*/
|
||||
public void setDefaultChannel(MessageChannel defaultChannel) {
|
||||
this.defaultChannel = defaultChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the timeout value to use for send operations.
|
||||
*
|
||||
* @param sendTimeout the send timeout in milliseconds
|
||||
*/
|
||||
public void setSendTimeout(long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the timeout value to use for receive operations.
|
||||
*
|
||||
* @param receiveTimeout the receive timeout in milliseconds
|
||||
*/
|
||||
public void setReceiveTimeout(long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a transaction manager to use for all exchange operations.
|
||||
* If none is provided, then the operations will occur without any
|
||||
* transactional behavior (i.e. there is no default transaction manager).
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public void setPropagationBehaviorName(String propagationBehaviorName) {
|
||||
this.propagationBehaviorName = propagationBehaviorName;
|
||||
}
|
||||
|
||||
public void setIsolationLevelName(String isolationLevelName) {
|
||||
this.isolationLevelName = isolationLevelName;
|
||||
}
|
||||
|
||||
public void setTransactionTimeout(int transactionTimeout) {
|
||||
this.transactionTimeout = transactionTimeout;
|
||||
}
|
||||
|
||||
public void setTransactionReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
private TransactionTemplate getTransactionTemplate() {
|
||||
if (!this.initialized) {
|
||||
this.afterPropertiesSet();
|
||||
}
|
||||
return this.transactionTemplate;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
if (this.transactionManager != null) {
|
||||
TransactionTemplate template = new TransactionTemplate(this.transactionManager);
|
||||
template.setPropagationBehaviorName(this.propagationBehaviorName);
|
||||
template.setIsolationLevelName(this.isolationLevelName);
|
||||
template.setTimeout(this.transactionTimeout);
|
||||
template.setReadOnly(this.readOnly);
|
||||
this.transactionTemplate = template;
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean send(final Message<?> message) {
|
||||
return this.send(message, this.getRequiredDefaultChannel());
|
||||
}
|
||||
|
||||
public boolean send(final Message<?> message, final MessageChannel channel) {
|
||||
TransactionTemplate txTemplate = this.getTransactionTemplate();
|
||||
if (txTemplate != null) {
|
||||
return txTemplate.execute(new TransactionCallback<Boolean>() {
|
||||
public Boolean doInTransaction(TransactionStatus status) {
|
||||
return doSend(message, channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.doSend(message, channel);
|
||||
}
|
||||
|
||||
public Message<?> receive() {
|
||||
MessageChannel channel = this.getRequiredDefaultChannel();
|
||||
Assert.state(channel instanceof PollableChannel,
|
||||
"The 'defaultChannel' must be a PollableChannel for receive operations.");
|
||||
return this.receive((PollableChannel) channel);
|
||||
}
|
||||
|
||||
public Message<?> receive(final PollableChannel channel) {
|
||||
TransactionTemplate txTemplate = this.getTransactionTemplate();
|
||||
if (txTemplate != null) {
|
||||
return txTemplate.execute(new TransactionCallback<Message<?>>() {
|
||||
public Message<?> doInTransaction(TransactionStatus status) {
|
||||
return doReceive(channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.doReceive(channel);
|
||||
}
|
||||
|
||||
public Message<?> sendAndReceive(final Message<?> request) {
|
||||
return this.sendAndReceive(request, this.getRequiredDefaultChannel());
|
||||
}
|
||||
|
||||
public Message<?> sendAndReceive(final Message<?> request, final MessageChannel channel) {
|
||||
TransactionTemplate txTemplate = this.getTransactionTemplate();
|
||||
if (txTemplate != null) {
|
||||
return txTemplate.execute(new TransactionCallback<Message<?>>() {
|
||||
public Message<?> doInTransaction(TransactionStatus status) {
|
||||
return doSendAndReceive(request, channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.doSendAndReceive(request, channel);
|
||||
}
|
||||
|
||||
private boolean doSend(Message<?> message, MessageChannel channel) {
|
||||
Assert.notNull(channel, "channel must not be null");
|
||||
long timeout = this.sendTimeout;
|
||||
boolean sent = (timeout >= 0)
|
||||
? channel.send(message, timeout)
|
||||
: channel.send(message);
|
||||
if (!sent && this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("failed to send message to channel '" + channel + "' within timeout: " + timeout);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
private Message<?> doReceive(PollableChannel channel) {
|
||||
Assert.notNull(channel, "channel must not be null");
|
||||
long timeout = this.receiveTimeout;
|
||||
Message<?> message = (timeout >= 0)
|
||||
? channel.receive(timeout)
|
||||
: channel.receive();
|
||||
if (message == null && this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("failed to receive message from channel '" + channel + "' within timeout: " + timeout);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private Message<?> doSendAndReceive(Message<?> request, MessageChannel channel) {
|
||||
Object originalReplyChannelHeader = request.getHeaders().getReplyChannel();
|
||||
Object originalErrorChannelHeader = request.getHeaders().getErrorChannel();
|
||||
TemporaryReplyChannel replyChannel = new TemporaryReplyChannel(this.receiveTimeout);
|
||||
request = MessageBuilder.fromMessage(request)
|
||||
.setReplyChannel(replyChannel)
|
||||
.setErrorChannel(replyChannel)
|
||||
.build();
|
||||
if (!this.doSend(request, channel)) {
|
||||
throw new MessageDeliveryException(request, "failed to send message to channel");
|
||||
}
|
||||
Message<?> reply = this.doReceive(replyChannel);
|
||||
if (reply != null) {
|
||||
reply = MessageBuilder.fromMessage(reply)
|
||||
.setHeader(MessageHeaders.REPLY_CHANNEL, originalReplyChannelHeader)
|
||||
.setHeader(MessageHeaders.ERROR_CHANNEL, originalErrorChannelHeader)
|
||||
.build();
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
private MessageChannel getRequiredDefaultChannel() {
|
||||
Assert.state(this.defaultChannel != null,
|
||||
"No 'defaultChannel' specified for MessageChannelTemplate. "
|
||||
+ "Unable to invoke methods without a channel argument.");
|
||||
return this.defaultChannel;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static class TemporaryReplyChannel implements PollableChannel {
|
||||
|
||||
private volatile Message<?> message;
|
||||
|
||||
private final long receiveTimeout;
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
|
||||
public TemporaryReplyChannel(long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return "temporaryReplyChannel";
|
||||
}
|
||||
|
||||
public Message receive() {
|
||||
return this.receive(-1);
|
||||
}
|
||||
|
||||
public Message receive(long timeout) {
|
||||
try {
|
||||
if (this.receiveTimeout < 0) {
|
||||
this.latch.await();
|
||||
}
|
||||
else {
|
||||
this.latch.await(this.receiveTimeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
return this.send(message, -1);
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message, long timeout) {
|
||||
this.message = message;
|
||||
this.latch.countDown();
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Message<?>> clear() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Message<?>> purge(MessageSelector selector) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.channel;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.core.MessagingException;
|
||||
import org.springframework.integration.message.ErrorMessage;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
* {@link ErrorHandler} implementation that sends an {@link ErrorMessage} to a
|
||||
* {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class MessagePublishingErrorHandler implements ErrorHandler, BeanFactoryAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile ChannelResolver channelResolver;
|
||||
|
||||
private volatile MessageChannel defaultErrorChannel;
|
||||
|
||||
private volatile long sendTimeout = 1000;
|
||||
|
||||
|
||||
public MessagePublishingErrorHandler() {
|
||||
}
|
||||
|
||||
public MessagePublishingErrorHandler(ChannelResolver channelResolver) {
|
||||
Assert.notNull(channelResolver, "channelResolver must not be null");
|
||||
this.channelResolver = channelResolver;
|
||||
}
|
||||
|
||||
|
||||
public void setDefaultErrorChannel(MessageChannel defaultErrorChannel) {
|
||||
this.defaultErrorChannel = defaultErrorChannel;
|
||||
}
|
||||
|
||||
public void setSendTimeout(long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "beanFactory must not be null");
|
||||
if (this.channelResolver == null) {
|
||||
this.channelResolver = new BeanFactoryChannelResolver(beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
public final void handleError(Throwable t) {
|
||||
Message<?> failedMessage = (t instanceof MessagingException) ?
|
||||
((MessagingException) t).getFailedMessage() : null;
|
||||
MessageChannel errorChannel = this.resolveErrorChannel(failedMessage);
|
||||
boolean sent = false;
|
||||
if (errorChannel != null) {
|
||||
try {
|
||||
if (this.sendTimeout >= 0) {
|
||||
sent = errorChannel.send(new ErrorMessage(t), this.sendTimeout);
|
||||
}
|
||||
else {
|
||||
sent = errorChannel.send(new ErrorMessage(t));
|
||||
}
|
||||
}
|
||||
catch (Throwable errorDeliveryError) { // message will be logged only
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Error message was not delivered.", errorDeliveryError);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sent && logger.isErrorEnabled()) {
|
||||
if (failedMessage != null) {
|
||||
logger.error("failure occurred in messaging task with message: " + failedMessage, t);
|
||||
}
|
||||
else {
|
||||
logger.error("failure occurred in messaging task", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MessageChannel resolveErrorChannel(Message<?> failedMessage) {
|
||||
if (this.defaultErrorChannel == null && this.channelResolver != null) {
|
||||
this.defaultErrorChannel = this.channelResolver.resolveChannelName(
|
||||
IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME);
|
||||
}
|
||||
if (failedMessage == null || failedMessage.getHeaders().getErrorChannel() == null) {
|
||||
return this.defaultErrorChannel;
|
||||
}
|
||||
Object errorChannelHeader = failedMessage.getHeaders().getErrorChannel();
|
||||
if (errorChannelHeader instanceof MessageChannel) {
|
||||
return (MessageChannel) errorChannelHeader;
|
||||
}
|
||||
Assert.isInstanceOf(String.class, errorChannelHeader,
|
||||
"Unsupported error channel header type. Expected MessageChannel or String, but actual type is [" +
|
||||
errorChannelHeader.getClass() + "]");
|
||||
return this.channelResolver.resolveChannelName((String) errorChannelHeader);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* A channel implementation that essentially behaves like "/dev/null".
|
||||
* All receive() calls will return <em>null</em>, and all send() calls
|
||||
* will return <em>true</em> although no action is performed.
|
||||
* Note however that the invocations are logged at debug-level.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class NullChannel implements PollableChannel {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("message sent to null channel: " + message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message, long timeout) {
|
||||
return this.send(message);
|
||||
}
|
||||
|
||||
public Message<?> receive() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("receive called on null channel");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message<?> receive(long timeout) {
|
||||
return this.receive();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
|
||||
/**
|
||||
* Interface for Message Channels from which Messages may be actively received through polling.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface PollableChannel extends MessageChannel {
|
||||
|
||||
/**
|
||||
* Receive a message from this channel, blocking indefinitely if necessary.
|
||||
*
|
||||
* @return the next available {@link Message} or <code>null</code> if interrupted
|
||||
*/
|
||||
Message<?> receive();
|
||||
|
||||
/**
|
||||
* Receive a message from this channel, blocking until either a message is
|
||||
* available or the specified timeout period elapses.
|
||||
*
|
||||
* @param timeout the timeout in milliseconds
|
||||
*
|
||||
* @return the next available {@link Message} or <code>null</code> if the
|
||||
* specified timeout period elapses or the message reception is interrupted
|
||||
*/
|
||||
Message<?> receive(long timeout);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageHeaders;
|
||||
import org.springframework.integration.core.MessagePriority;
|
||||
import org.springframework.integration.util.UpperBound;
|
||||
|
||||
/**
|
||||
* A message channel that prioritizes messages based on a {@link Comparator}.
|
||||
* The default comparator is based upon the message header's 'priority'.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PriorityChannel extends QueueChannel {
|
||||
|
||||
private final UpperBound upperBound;
|
||||
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity. If the capacity
|
||||
* is a non-positive value, the queue will be unbounded. Message priority
|
||||
* will be determined by the provided {@link Comparator}. If the comparator
|
||||
* is <code>null</code>, the priority will be based upon the value of
|
||||
* {@link MessageHeaders#getPriority()}.
|
||||
*/
|
||||
public PriorityChannel(int capacity, Comparator<Message<?>> comparator) {
|
||||
super(new PriorityBlockingQueue<Message<?>>(11,
|
||||
(comparator != null) ? comparator : new MessagePriorityComparator()));
|
||||
this.upperBound = new UpperBound(capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity. Message priority
|
||||
* will be based upon the value of {@link MessageHeaders#getPriority()}.
|
||||
*/
|
||||
public PriorityChannel(int capacity) {
|
||||
this(capacity, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with an unbounded queue. Message priority will be
|
||||
* determined by the provided {@link Comparator}. If the comparator
|
||||
* is <code>null</code>, the priority will be based upon the value of
|
||||
* {@link MessageHeaders#getPriority()}.
|
||||
*/
|
||||
public PriorityChannel(Comparator<Message<?>> comparator) {
|
||||
this(0, comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with an unbounded queue. Message priority will be
|
||||
* based on the value of {@link MessageHeaders#getPriority()}.
|
||||
*/
|
||||
public PriorityChannel() {
|
||||
this(0, null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
if (!upperBound.tryAcquire(timeout)) {
|
||||
return false;
|
||||
}
|
||||
return super.doSend(message, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message<?> doReceive(long timeout) {
|
||||
Message<?> message = super.doReceive(timeout);
|
||||
if (message != null) {
|
||||
upperBound.release();
|
||||
return message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class MessagePriorityComparator implements Comparator<Message<?>> {
|
||||
|
||||
public int compare(Message<?> message1, Message<?> message2) {
|
||||
MessagePriority priority1 = message1.getHeaders().getPriority();
|
||||
MessagePriority priority2 = message2.getHeaders().getPriority();
|
||||
priority1 = priority1 != null ? priority1 : MessagePriority.NORMAL;
|
||||
priority2 = priority2 != null ? priority2 : MessagePriority.NORMAL;
|
||||
return priority1.compareTo(priority2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.integration.dispatcher.BroadcastingDispatcher;
|
||||
import org.springframework.integration.util.ErrorHandlingTaskExecutor;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
* A channel that sends Messages to each of its subscribers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PublishSubscribeChannel extends AbstractSubscribableChannel {
|
||||
|
||||
private volatile BroadcastingDispatcher dispatcher;
|
||||
|
||||
private volatile Executor executor;
|
||||
|
||||
private volatile ErrorHandler errorHandler;
|
||||
|
||||
private volatile boolean ignoreFailures;
|
||||
|
||||
private volatile boolean applySequence;
|
||||
|
||||
|
||||
/**
|
||||
* Create a PublishSubscribeChannel that will use an {@link Executor}
|
||||
* to invoke the handlers. If this is null, each invocation will occur in
|
||||
* the message sender's thread.
|
||||
*/
|
||||
public PublishSubscribeChannel(Executor executor) {
|
||||
this.executor = executor;
|
||||
this.dispatcher = new BroadcastingDispatcher(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PublishSubscribeChannel that will invoke the handlers in the
|
||||
* message sender's thread.
|
||||
*/
|
||||
public PublishSubscribeChannel() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provide an {@link ErrorHandler} strategy for handling Exceptions that
|
||||
* occur downstream from this channel. This will <i>only</i> be applied if
|
||||
* an Executor has been configured to dispatch the Messages for this
|
||||
* channel. Otherwise, Exceptions will be thrown directly within the
|
||||
* sending Thread. If no ErrorHandler is provided, and this channel does
|
||||
* delegate its dispatching to an Executor, the default strategy is
|
||||
* a {@link MessagePublishingErrorHandler} that sends error messages to
|
||||
* the failed request Message's error channel header if available or to
|
||||
* the default 'errorChannel' otherwise.
|
||||
* @see #PublishSubscribeChannel(Executor)
|
||||
*/
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether failures for one or more of the handlers should be
|
||||
* ignored. By default this is <code>false</code> meaning that an Exception
|
||||
* will be thrown whenever a handler fails. To override this and suppress
|
||||
* Exceptions, set the value to <code>true</code>.
|
||||
*/
|
||||
public void setIgnoreFailures(boolean ignoreFailures) {
|
||||
this.ignoreFailures = ignoreFailures;
|
||||
this.getDispatcher().setIgnoreFailures(ignoreFailures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to apply the sequence number and size headers to the
|
||||
* messages prior to invoking the subscribed handlers. By default, this
|
||||
* value is <code>false</code> meaning that sequence headers will
|
||||
* <em>not</em> be applied. If planning to use an Aggregator downstream
|
||||
* with the default correlation and completion strategies, you should set
|
||||
* this flag to <code>true</code>.
|
||||
*/
|
||||
public void setApplySequence(boolean applySequence) {
|
||||
this.applySequence = applySequence;
|
||||
this.getDispatcher().setApplySequence(applySequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for initialization.
|
||||
*/
|
||||
@Override
|
||||
public final void onInit() {
|
||||
if (this.executor != null) {
|
||||
if (!(this.executor instanceof ErrorHandlingTaskExecutor)) {
|
||||
if (this.errorHandler == null) {
|
||||
this.errorHandler = new MessagePublishingErrorHandler(
|
||||
new BeanFactoryChannelResolver(this.getBeanFactory()));
|
||||
}
|
||||
this.executor = new ErrorHandlingTaskExecutor(this.executor, this.errorHandler);
|
||||
}
|
||||
this.dispatcher = new BroadcastingDispatcher(this.executor);
|
||||
this.dispatcher.setIgnoreFailures(this.ignoreFailures);
|
||||
this.dispatcher.setApplySequence(this.applySequence);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BroadcastingDispatcher getDispatcher() {
|
||||
return this.dispatcher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.selector.MessageSelector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple implementation of a message channel. Each {@link Message} is placed in
|
||||
* a {@link BlockingQueue} whose capacity may be specified upon construction.
|
||||
* The capacity must be a positive integer value. For a zero-capacity version
|
||||
* based upon a {@link java.util.concurrent.SynchronousQueue}, consider the
|
||||
* {@link RendezvousChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class QueueChannel extends AbstractPollableChannel {
|
||||
|
||||
private final BlockingQueue<Message<?>> queue;
|
||||
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue.
|
||||
*/
|
||||
public QueueChannel(BlockingQueue<Message<?>> queue) {
|
||||
Assert.notNull(queue, "'queue' must not be null");
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with the specified queue capacity.
|
||||
*/
|
||||
public QueueChannel(int capacity) {
|
||||
Assert.isTrue(capacity > 0, "The capacity must be a positive integer. " +
|
||||
"For a zero-capacity alternative, consider using a 'RendezvousChannel'.");
|
||||
this.queue = new LinkedBlockingQueue<Message<?>>(capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel with "unbounded" queue capacity. The actual capacity value is
|
||||
* {@link Integer#MAX_VALUE}. Note that a bounded queue is recommended, since an
|
||||
* unbounded queue may lead to OutOfMemoryErrors.
|
||||
*/
|
||||
public QueueChannel() {
|
||||
this(new LinkedBlockingQueue<Message<?>>());
|
||||
}
|
||||
|
||||
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
Assert.notNull(message, "'message' must not be null");
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
return this.queue.offer(message, timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (timeout == 0) {
|
||||
return this.queue.offer(message);
|
||||
}
|
||||
queue.put(message);
|
||||
return true;
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected Message<?> doReceive(long timeout) {
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
return queue.poll(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (timeout == 0) {
|
||||
return queue.poll();
|
||||
}
|
||||
return queue.take();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all {@link Message Messages} from this channel.
|
||||
*/
|
||||
public List<Message<?>> clear() {
|
||||
List<Message<?>> clearedMessages = new ArrayList<Message<?>>();
|
||||
this.queue.drainTo(clearedMessages);
|
||||
return clearedMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any {@link Message Messages} that are not accepted by the provided selector.
|
||||
*/
|
||||
public List<Message<?>> purge(MessageSelector selector) {
|
||||
if (selector == null) {
|
||||
return this.clear();
|
||||
}
|
||||
List<Message<?>> purgedMessages = new ArrayList<Message<?>>();
|
||||
Object[] array = this.queue.toArray();
|
||||
for (Object o : array) {
|
||||
Message<?> message = (Message<?>) o;
|
||||
if (!selector.accept(message) && this.queue.remove(message)) {
|
||||
purgedMessages.add(message);
|
||||
}
|
||||
}
|
||||
return purgedMessages;
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
return this.queue.size();
|
||||
}
|
||||
|
||||
public int getRemainingCapacity() {
|
||||
return this.queue.remainingCapacity();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel;
|
||||
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* A zero-capacity version of {@link QueueChannel} that delegates to a
|
||||
* {@link SynchronousQueue} internally. This accommodates "handoff" scenarios
|
||||
* (i.e. blocking while waiting for another party to send or receive).
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class RendezvousChannel extends QueueChannel {
|
||||
|
||||
public RendezvousChannel() {
|
||||
super(new SynchronousQueue<Message<?>>());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
|
||||
/**
|
||||
* Interface for any MessageChannel implementation that accepts subscribers.
|
||||
* The subscribers must implement the {@link MessageHandler} interface and
|
||||
* will be invoked when a Message is available.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface SubscribableChannel extends MessageChannel {
|
||||
|
||||
/**
|
||||
* Register a {@link MessageHandler} as a subscriber to this channel.
|
||||
*/
|
||||
boolean subscribe(MessageHandler handler);
|
||||
|
||||
/**
|
||||
* Remove a {@link MessageHandler} from the subscribers of this channel.
|
||||
*/
|
||||
boolean unsubscribe(MessageHandler handler);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
|
||||
/**
|
||||
* A channel implementation that stores messages in a thread-bound queue. In
|
||||
* other words, send() will put a message at the tail of the queue for the
|
||||
* current thread, and receive() will retrieve a message from the head of the
|
||||
* queue. Since, by definition, only one thread will interact with the queue
|
||||
* at a time, the timeout values on send and receive have no effect. If there
|
||||
* are no Messages in the queue, the receive operations will return a
|
||||
* <code>null</code> value immediately, regardless of any timeout value.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ThreadLocalChannel extends AbstractPollableChannel {
|
||||
|
||||
private final ThreadLocalMessageHolder messageHolder = new ThreadLocalMessageHolder();
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
if (message == null) {
|
||||
return false;
|
||||
}
|
||||
return messageHolder.get().add(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message<?> doReceive(long timeout) {
|
||||
return messageHolder.get().poll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The thread-bound Queue.
|
||||
*/
|
||||
private static class ThreadLocalMessageHolder extends ThreadLocal<Queue<Message<?>>> {
|
||||
|
||||
@Override
|
||||
protected Queue<Message<?>> initialValue() {
|
||||
return new LinkedBlockingQueue<Message<?>>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel.interceptor;
|
||||
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
|
||||
/**
|
||||
* A {@link ChannelInterceptor} with no-op method implementations so that
|
||||
* subclasses do not have to implement all of the interface's methods.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ChannelInterceptorAdapter implements ChannelInterceptor {
|
||||
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
}
|
||||
|
||||
public boolean preReceive(MessageChannel channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel.interceptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Will apply global interceptors to channels (<channel-interceptor-chain>). Since global interceptors
|
||||
* could be Ordered or un-Ordered they will be sorted before merged with other interceptors in the channel.
|
||||
* Sorting will only be done within the given interceptor chain which itself defines 'order' attribute
|
||||
* essentially creating a group of ordered interceptors which are ordered internally and then these chain
|
||||
* groups are also ordered. For example:
|
||||
* <pre>
|
||||
* channel-interceptor-chain channel-name-pattern="foo" order="5" - positive order value means AFTER local channel interceptors
|
||||
* Ordered-global interceptor (4)
|
||||
* Ordered-global interceptor (1)
|
||||
* channel-interceptor-chain
|
||||
* channel-interceptor-chain channel-name-pattern="foo" order="-1" - negative order value means AFTER local channel interceptors
|
||||
* Ordered-global interceptor (3)
|
||||
* Ordered-global interceptor (10)
|
||||
* channel-interceptor-chain
|
||||
*
|
||||
* channel id="foo"
|
||||
* Ordered-in-channel interceptor (1)
|
||||
* channel
|
||||
*
|
||||
* will result in channel with the following interceptors
|
||||
* Channel "foo"
|
||||
* Ordered-global interceptor (3)
|
||||
* Ordered-global interceptor (10)
|
||||
* Ordered-in-channel interceptor (1)
|
||||
* Ordered-global interceptor (1)
|
||||
* Ordered-global interceptor (4)
|
||||
* </pre>
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.0
|
||||
*/
|
||||
final class GlobalChannelInterceptorBeanPostProcessor implements BeanPostProcessor, InitializingBean{
|
||||
private final static Log logger = LogFactory.getLog(GlobalChannelInterceptorBeanPostProcessor.class);
|
||||
private final OrderComparator comparator = new OrderComparator();
|
||||
private List<String> allAvailablePatters;
|
||||
private List<GlobalChannelInterceptorChain> globalInterceptors;
|
||||
private final Map<String, Pattern> compiledPatterns = new HashMap<String, Pattern>();
|
||||
|
||||
private List<GlobalChannelInterceptorChain> positiveOrderChains = new ArrayList<GlobalChannelInterceptorChain>();
|
||||
private List<GlobalChannelInterceptorChain> negativeOrderChains = new ArrayList<GlobalChannelInterceptorChain>();
|
||||
/**
|
||||
*
|
||||
* @param globalInterceptors
|
||||
*/
|
||||
GlobalChannelInterceptorBeanPostProcessor(List<GlobalChannelInterceptorChain> globalInterceptors){
|
||||
this.globalInterceptors = globalInterceptors;
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (channelPatternMatches(beanName)){
|
||||
if (bean instanceof AbstractMessageChannel){
|
||||
logger.debug("Applying global interceptors on channel '" + beanName + "'");
|
||||
this.mergeInterceptorsToChannel((AbstractMessageChannel) bean, beanName);
|
||||
} else {
|
||||
logger.warn("Attempt to add channel interceptors is unsuccessfull. Global channel interceptors " +
|
||||
"can only be added to AbstractMessageChannel. Current implementation is: " + bean.getClass() +
|
||||
" This might happen becouse you specified a single wild-card '*' in 'channel-name-pattern'");
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param channel
|
||||
* @param channelName
|
||||
*/
|
||||
private void mergeInterceptorsToChannel(AbstractMessageChannel channel, String channelName){
|
||||
List<ChannelInterceptor> tInt = null;
|
||||
List<ChannelInterceptor> interceptors = this.getExistingInterceptors(channel);
|
||||
// POSITIVE
|
||||
List<GlobalChannelInterceptorChain> tempPositiveInterceptorChains = new ArrayList<GlobalChannelInterceptorChain>();
|
||||
for (GlobalChannelInterceptorChain positiveOrderChain : positiveOrderChains) {
|
||||
if (channelPatternMatches(channelName, positiveOrderChain.getPatterns())){
|
||||
tempPositiveInterceptorChains.add(positiveOrderChain);
|
||||
}
|
||||
}
|
||||
// sort chain
|
||||
Collections.sort(tempPositiveInterceptorChains, comparator);
|
||||
|
||||
for (GlobalChannelInterceptorChain globalChannelInterceptorChain : tempPositiveInterceptorChains) {
|
||||
tInt = globalChannelInterceptorChain.getInterceptors();
|
||||
// sort within the chain
|
||||
Collections.sort(tInt, comparator);
|
||||
interceptors.addAll(tInt);
|
||||
}
|
||||
// NEGATIVE
|
||||
List<GlobalChannelInterceptorChain> tempNegativeInterceptorChains = new ArrayList<GlobalChannelInterceptorChain>();
|
||||
for (GlobalChannelInterceptorChain negativeOrderChain : negativeOrderChains) {
|
||||
if (channelPatternMatches(channelName, negativeOrderChain.getPatterns())){
|
||||
tempNegativeInterceptorChains.add(negativeOrderChain);
|
||||
}
|
||||
}
|
||||
// sort chains
|
||||
Collections.sort(tempNegativeInterceptorChains, comparator);
|
||||
|
||||
for (GlobalChannelInterceptorChain globalChannelInterceptorChain : tempNegativeInterceptorChains) {
|
||||
tInt = globalChannelInterceptorChain.getInterceptors();
|
||||
// sort within the chain
|
||||
Collections.sort(tInt, comparator);
|
||||
interceptors.addAll(0, tInt);
|
||||
}
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
private void filterPositiveNegativeOrderChains(){
|
||||
for (GlobalChannelInterceptorChain globalInterceptorChain : globalInterceptors) {
|
||||
if (globalInterceptorChain.getOrder() < 0){
|
||||
negativeOrderChains.add(globalInterceptorChain);
|
||||
} else {
|
||||
positiveOrderChains.add(globalInterceptorChain);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<ChannelInterceptor> getExistingInterceptors(AbstractMessageChannel channel){
|
||||
DirectFieldAccessor channelAccessor = new DirectFieldAccessor(channel);
|
||||
Object iWrapper = channelAccessor.getPropertyValue("interceptors");
|
||||
DirectFieldAccessor iWrapperAccessor = new DirectFieldAccessor(iWrapper);
|
||||
List<ChannelInterceptor> interceptors = (List<ChannelInterceptor>) iWrapperAccessor.getPropertyValue("interceptors");
|
||||
return interceptors;
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
private boolean channelPatternMatches(String beanName, String... patternsToMatch){
|
||||
String[] patterns = null;
|
||||
if (patternsToMatch.length > 0){
|
||||
patterns = patternsToMatch;
|
||||
} else {
|
||||
patterns = allAvailablePatters.toArray(new String[]{});
|
||||
}
|
||||
for (String channelPattern : patterns) {
|
||||
channelPattern = channelPattern.trim();
|
||||
if (channelPattern.trim().equals("*")){
|
||||
return true;
|
||||
}
|
||||
Pattern p = compiledPatterns.get(channelPattern);
|
||||
if (p == null){
|
||||
p = Pattern.compile(channelPattern);
|
||||
compiledPatterns.put(channelPattern, p);
|
||||
}
|
||||
Matcher m = p.matcher(beanName);
|
||||
if (m.find()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
allAvailablePatters = new ArrayList<String>();
|
||||
for (GlobalChannelInterceptorChain globalInterceptorchain : globalInterceptors) {
|
||||
allAvailablePatters.addAll(CollectionUtils.arrayToList(globalInterceptorchain.getPatterns()));
|
||||
}
|
||||
this.filterPositiveNegativeOrderChains();
|
||||
logger.info("Initialized: '" + this.getClass().getSimpleName() + "' to apply global channel interceptors");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel.interceptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.0
|
||||
*/
|
||||
final class GlobalChannelInterceptorChain implements Ordered{
|
||||
private List<ChannelInterceptor> interceptors;
|
||||
private String[] patterns;
|
||||
|
||||
private int order;
|
||||
|
||||
public GlobalChannelInterceptorChain(List<ChannelInterceptor> interceptors, String[] patterns, int order){
|
||||
this.interceptors = interceptors;
|
||||
this.patterns = patterns;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
List<ChannelInterceptor> getInterceptors(){
|
||||
return interceptors;
|
||||
}
|
||||
|
||||
String[] getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return interceptors.toString();
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.channel.interceptor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.selector.MessageSelector;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.integration.channel.ChannelInterceptor} that
|
||||
* delegates to a list of {@link MessageSelector MessageSelectors} to decide
|
||||
* whether a {@link Message} should be accepted on the {@link MessageChannel}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MessageSelectingInterceptor extends ChannelInterceptorAdapter {
|
||||
|
||||
private final List<MessageSelector> selectors;
|
||||
|
||||
|
||||
public MessageSelectingInterceptor(MessageSelector... selectors) {
|
||||
this.selectors = Arrays.asList(selectors);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
for (MessageSelector selector : this.selectors) {
|
||||
if (!selector.accept(message)) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"selector '" + selector + "' did not accept message");
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.channel.interceptor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.channel.ChannelInterceptor;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.selector.MessageSelector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ChannelInterceptor} that publishes a copy of the intercepted message
|
||||
* to a secondary target while still sending the original message to the main channel.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class WireTap extends ChannelInterceptorAdapter implements Lifecycle {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WireTap.class);
|
||||
|
||||
private final MessageChannel channel;
|
||||
|
||||
private volatile long timeout = 0;
|
||||
|
||||
private final MessageSelector selector;
|
||||
|
||||
private volatile boolean running = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new wire tap with <em>no</em> {@link MessageSelector}.
|
||||
*
|
||||
* @param channel the MessageChannel to which intercepted messages will be sent
|
||||
*/
|
||||
public WireTap(MessageChannel channel) {
|
||||
this(channel, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new wire tap with the provided {@link MessageSelector}.
|
||||
*
|
||||
* @param channel the channel to which intercepted messages will be sent
|
||||
* @param selector the selector that must accept a message for it to be
|
||||
* sent to the intercepting channel
|
||||
*/
|
||||
public WireTap(MessageChannel channel, MessageSelector selector) {
|
||||
Assert.notNull(channel, "channel must not be null");
|
||||
this.channel = channel;
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the timeout value for sending to the intercepting target.
|
||||
*
|
||||
* @param timeout the timeout in milliseconds
|
||||
*/
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the wire tap is currently running.
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the wire tap if it has been stopped. It is running by default.
|
||||
*/
|
||||
public void start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the wire tap. To restart, invoke {@link #start()}.
|
||||
*/
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept the Message and, <em>if accepted</em> by the {@link MessageSelector},
|
||||
* send it to the secondary target. If this wire tap's {@link MessageSelector} is
|
||||
* <code>null</code>, it will accept all messages.
|
||||
*/
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
if (this.running && (this.selector == null || this.selector.accept(message))) {
|
||||
boolean sent = (this.timeout >= 0)
|
||||
? this.channel.send(message, this.timeout)
|
||||
: this.channel.send(message);
|
||||
if (!sent && logger.isWarnEnabled()) {
|
||||
logger.warn("failed to send message to WireTap channel '" + this.channel + "'");
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for FactoryBeans that create MessageHandler instances.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Alexander Peters
|
||||
*/
|
||||
abstract class AbstractMessageHandlerFactoryBean implements FactoryBean<MessageHandler>, BeanFactoryAware {
|
||||
|
||||
private volatile MessageHandler handler;
|
||||
|
||||
private volatile Object targetObject;
|
||||
|
||||
private volatile String targetMethodName;
|
||||
|
||||
private volatile String expression;
|
||||
|
||||
private volatile MessageChannel outputChannel;
|
||||
|
||||
private volatile Integer order;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
public void setTargetObject(Object targetObject) {
|
||||
this.targetObject = targetObject;
|
||||
}
|
||||
|
||||
public void setTargetMethodName(String targetMethodName) {
|
||||
this.targetMethodName = targetMethodName;
|
||||
}
|
||||
|
||||
public void setExpression(String expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
public void setOutputChannel(MessageChannel outputChannel) {
|
||||
this.outputChannel = outputChannel;
|
||||
}
|
||||
|
||||
public void setOrder(Integer order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public MessageHandler getObject() throws Exception {
|
||||
if (this.handler == null) {
|
||||
this.initializeHandler();
|
||||
Assert.notNull(this.handler, "failed to create MessageHandler");
|
||||
if (this.handler instanceof AbstractReplyProducingMessageHandler && this.outputChannel != null) {
|
||||
((AbstractReplyProducingMessageHandler) this.handler).setOutputChannel(this.outputChannel);
|
||||
}
|
||||
if (this.handler instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.handler).setBeanFactory(beanFactory);
|
||||
}
|
||||
if (this.handler instanceof AbstractMessageHandler && this.order != null) {
|
||||
((AbstractMessageHandler) this.handler).setOrder(this.order.intValue());
|
||||
}
|
||||
}
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
public Class<? extends MessageHandler> getObjectType() {
|
||||
if (this.handler != null) {
|
||||
return this.handler.getClass();
|
||||
}
|
||||
return MessageHandler.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initializeHandler() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
if (this.targetObject == null) {
|
||||
Assert.isTrue(!StringUtils.hasText(this.targetMethodName),
|
||||
"The target method is only allowed when a target object (ref or inner bean) is also provided.");
|
||||
}
|
||||
if (this.targetObject != null) {
|
||||
Assert.state(this.expression == null,
|
||||
"The 'targetObject' and 'expression' properties are mutually exclusive.");
|
||||
this.handler = this.createMethodInvokingHandler(this.targetObject, this.targetMethodName);
|
||||
}
|
||||
else if (this.expression != null) {
|
||||
this.handler = this.createExpressionEvaluatingHandler(this.expression);
|
||||
}
|
||||
else {
|
||||
this.handler = this.createDefaultHandler();
|
||||
}
|
||||
if (this.handler instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.handler).setBeanFactory(beanFactory);
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
if (this.handler instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) this.handler).afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new BeanInitializationException("failed to initialize MessageHandler", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to create the MessageHandler.
|
||||
*/
|
||||
abstract MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName);
|
||||
|
||||
MessageHandler createExpressionEvaluatingHandler(String expression) {
|
||||
throw new UnsupportedOperationException(this.getClass().getName() + " does not support expressions.");
|
||||
}
|
||||
|
||||
MessageHandler createDefaultHandler() {
|
||||
throw new IllegalArgumentException(
|
||||
"Exactly one of the 'targetObject' or 'expression' property is required.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.channel.PollableChannel;
|
||||
import org.springframework.integration.channel.SubscribableChannel;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.endpoint.PollingConsumer;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ConsumerEndpointFactoryBean
|
||||
implements FactoryBean, BeanFactoryAware, BeanNameAware, InitializingBean, SmartLifecycle {
|
||||
|
||||
private volatile MessageHandler handler;
|
||||
|
||||
private volatile String beanName;
|
||||
|
||||
private volatile String inputChannelName;
|
||||
|
||||
private volatile PollerMetadata pollerMetadata;
|
||||
|
||||
private volatile boolean autoStartup = true;
|
||||
|
||||
private volatile ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private volatile AbstractEndpoint endpoint;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
private final Object handlerMonitor = new Object();
|
||||
|
||||
|
||||
public void setHandler(MessageHandler handler) {
|
||||
Assert.notNull(handler, "handler must not be null");
|
||||
synchronized (this.handlerMonitor) {
|
||||
Assert.isNull(this.handler, "handler cannot be overridden");
|
||||
this.handler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setInputChannelName(String inputChannelName) {
|
||||
this.inputChannelName = inputChannelName;
|
||||
}
|
||||
|
||||
public void setPollerMetadata(PollerMetadata pollerMetadata) {
|
||||
this.pollerMetadata = pollerMetadata;
|
||||
}
|
||||
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory,
|
||||
"a ConfigurableBeanFactory is required");
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.initializeEndpoint();
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
if (!this.initialized) {
|
||||
this.initializeEndpoint();
|
||||
}
|
||||
return this.endpoint;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
if (this.endpoint == null) {
|
||||
return AbstractEndpoint.class;
|
||||
}
|
||||
return this.endpoint.getClass();
|
||||
}
|
||||
|
||||
private void initializeEndpoint() throws Exception {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
Assert.hasText(this.inputChannelName, "inputChannelName is required");
|
||||
Assert.isTrue(this.beanFactory.containsBean(this.inputChannelName),
|
||||
"no such input channel '" + this.inputChannelName + "' for endpoint '" + this.beanName + "'");
|
||||
MessageChannel channel = (MessageChannel)
|
||||
this.beanFactory.getBean(this.inputChannelName, MessageChannel.class);
|
||||
if (channel instanceof SubscribableChannel) {
|
||||
Assert.isNull(this.pollerMetadata, "A poller should not be specified for endpoint '" + this.beanName
|
||||
+ "', since '" + this.inputChannelName + "' is a SubscribableChannel (not pollable).");
|
||||
this.endpoint = new EventDrivenConsumer((SubscribableChannel) channel, this.handler);
|
||||
}
|
||||
else if (channel instanceof PollableChannel) {
|
||||
PollingConsumer pollingConsumer = new PollingConsumer(
|
||||
(PollableChannel) channel, this.handler);
|
||||
if (this.pollerMetadata == null) {
|
||||
this.pollerMetadata = IntegrationContextUtils.getDefaultPollerMetadata(this.beanFactory);
|
||||
Assert.notNull(this.pollerMetadata, "No poller has been defined for endpoint '"
|
||||
+ this.beanName + "', and no default poller is available within the context.");
|
||||
}
|
||||
pollingConsumer.setTrigger(this.pollerMetadata.getTrigger());
|
||||
pollingConsumer.setMaxMessagesPerPoll(this.pollerMetadata.getMaxMessagesPerPoll());
|
||||
pollingConsumer.setReceiveTimeout(this.pollerMetadata.getReceiveTimeout());
|
||||
pollingConsumer.setTaskExecutor(this.pollerMetadata.getTaskExecutor());
|
||||
pollingConsumer.setTransactionManager(this.pollerMetadata.getTransactionManager());
|
||||
pollingConsumer.setTransactionDefinition(this.pollerMetadata.getTransactionDefinition());
|
||||
pollingConsumer.setAdviceChain(this.pollerMetadata.getAdviceChain());
|
||||
this.endpoint = pollingConsumer;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"unsupported channel type: [" + channel.getClass() + "]");
|
||||
}
|
||||
this.endpoint.setBeanName(this.beanName);
|
||||
this.endpoint.setBeanFactory(this.beanFactory);
|
||||
this.endpoint.setAutoStartup(this.autoStartup);
|
||||
this.endpoint.afterPropertiesSet();
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SmartLifecycle implementation (delegates to the created endpoint)
|
||||
*/
|
||||
|
||||
public boolean isAutoStartup() {
|
||||
return (this.endpoint != null) ? this.endpoint.isAutoStartup() : true;
|
||||
}
|
||||
|
||||
public int getPhase() {
|
||||
return (this.endpoint != null) ? this.endpoint.getPhase() : 0;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return (this.endpoint != null) ? this.endpoint.isRunning() : false;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (this.endpoint != null) {
|
||||
this.endpoint.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.endpoint != null) {
|
||||
this.endpoint.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) {
|
||||
if (this.endpoint != null) {
|
||||
this.endpoint.stop(callback);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
|
||||
import org.springframework.integration.filter.MessageFilter;
|
||||
import org.springframework.integration.filter.MethodInvokingSelector;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.selector.MessageSelector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory bean for creating a Message Filter.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FilterFactoryBean extends AbstractMessageHandlerFactoryBean {
|
||||
|
||||
private volatile MessageChannel discardChannel;
|
||||
|
||||
private volatile Boolean throwExceptionOnRejection;
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
|
||||
public void setDiscardChannel(MessageChannel discardChannel) {
|
||||
this.discardChannel = discardChannel;
|
||||
}
|
||||
|
||||
public void setThrowExceptionOnRejection(Boolean throwExceptionOnRejection) {
|
||||
this.throwExceptionOnRejection = throwExceptionOnRejection;
|
||||
}
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
|
||||
MessageSelector selector = null;
|
||||
if (targetObject instanceof MessageSelector) {
|
||||
selector = (MessageSelector) targetObject;
|
||||
}
|
||||
else if (StringUtils.hasText(targetMethodName)) {
|
||||
selector = new MethodInvokingSelector(targetObject, targetMethodName);
|
||||
}
|
||||
else {
|
||||
selector = new MethodInvokingSelector(targetObject);
|
||||
}
|
||||
return this.createFilter(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createExpressionEvaluatingHandler(String expression) {
|
||||
return this.createFilter(new ExpressionEvaluatingSelector(expression));
|
||||
}
|
||||
|
||||
private MessageFilter createFilter(MessageSelector selector) {
|
||||
MessageFilter filter = new MessageFilter(selector);
|
||||
if (this.throwExceptionOnRejection != null) {
|
||||
filter.setThrowExceptionOnRejection(this.throwExceptionOnRejection);
|
||||
}
|
||||
if (this.discardChannel != null) {
|
||||
filter.setDiscardChannel(discardChannel);
|
||||
}
|
||||
if (this.sendTimeout != null) {
|
||||
filter.setSendTimeout(this.sendTimeout.longValue());
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
import org.springframework.integration.channel.ChannelResolver;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.router.AbstractChannelNameResolvingMessageRouter;
|
||||
import org.springframework.integration.router.AbstractMessageRouter;
|
||||
import org.springframework.integration.router.ExpressionEvaluatingRouter;
|
||||
import org.springframework.integration.router.MethodInvokingRouter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory bean for creating a Message Router.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Jonas Partner
|
||||
*/
|
||||
public class RouterFactoryBean extends AbstractMessageHandlerFactoryBean {
|
||||
|
||||
private volatile ChannelResolver channelResolver;
|
||||
|
||||
private volatile MessageChannel defaultOutputChannel;
|
||||
|
||||
private volatile Long timeout;
|
||||
|
||||
private volatile Boolean resolutionRequired;
|
||||
|
||||
private volatile Boolean ignoreChannelNameResolutionFailures;
|
||||
|
||||
private volatile Boolean applySequence;
|
||||
|
||||
private volatile Boolean ignoreSendFailures;
|
||||
|
||||
|
||||
public void setChannelResolver(ChannelResolver channelResolver) {
|
||||
this.channelResolver = channelResolver;
|
||||
}
|
||||
|
||||
public void setDefaultOutputChannel(MessageChannel defaultOutputChannel) {
|
||||
this.defaultOutputChannel = defaultOutputChannel;
|
||||
}
|
||||
|
||||
public void setTimeout(Long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public void setResolutionRequired(Boolean resolutionRequired) {
|
||||
this.resolutionRequired = resolutionRequired;
|
||||
}
|
||||
|
||||
public void setIgnoreChannelNameResolutionFailures(Boolean ignoreChannelNameResolutionFailures) {
|
||||
this.ignoreChannelNameResolutionFailures = ignoreChannelNameResolutionFailures;
|
||||
}
|
||||
|
||||
public void setApplySequence(Boolean applySequence) {
|
||||
this.applySequence = applySequence;
|
||||
}
|
||||
|
||||
public void setIgnoreSendFailures(Boolean ignoreSendFailures) {
|
||||
this.ignoreSendFailures = ignoreSendFailures;
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
|
||||
Assert.notNull(targetObject, "target object must not be null");
|
||||
AbstractMessageRouter router = this.createRouter(targetObject, targetMethodName);
|
||||
return this.configureRouter(router);
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createExpressionEvaluatingHandler(String expression) {
|
||||
return this.configureRouter(new ExpressionEvaluatingRouter(expression));
|
||||
}
|
||||
|
||||
private AbstractMessageRouter createRouter(Object targetObject, String targetMethodName) {
|
||||
if (targetObject instanceof AbstractMessageRouter) {
|
||||
Assert.isTrue(!StringUtils.hasText(targetMethodName),
|
||||
"target method should not be provided when the target " +
|
||||
"object is an implementation of AbstractMessageRouter");
|
||||
return (AbstractMessageRouter) targetObject;
|
||||
}
|
||||
MethodInvokingRouter router = (StringUtils.hasText(targetMethodName))
|
||||
? new MethodInvokingRouter(targetObject, targetMethodName)
|
||||
: new MethodInvokingRouter(targetObject);
|
||||
return router;
|
||||
}
|
||||
|
||||
private AbstractMessageRouter configureRouter(AbstractMessageRouter router) {
|
||||
if (this.channelResolver != null &&
|
||||
router instanceof AbstractChannelNameResolvingMessageRouter) {
|
||||
((AbstractChannelNameResolvingMessageRouter) router).setChannelResolver(this.channelResolver);
|
||||
}
|
||||
if (this.defaultOutputChannel != null) {
|
||||
router.setDefaultOutputChannel(this.defaultOutputChannel);
|
||||
}
|
||||
if (this.timeout != null) {
|
||||
router.setTimeout(timeout.longValue());
|
||||
}
|
||||
if (this.ignoreChannelNameResolutionFailures != null) {
|
||||
Assert.isTrue(router instanceof AbstractChannelNameResolvingMessageRouter,
|
||||
"The 'ignoreChannelNameResolutionFailures' property can only be set on routers that extend "
|
||||
+ AbstractChannelNameResolvingMessageRouter.class.getName());
|
||||
((AbstractChannelNameResolvingMessageRouter) router).setIgnoreChannelNameResolutionFailures(ignoreChannelNameResolutionFailures);
|
||||
}
|
||||
if (this.applySequence != null) {
|
||||
router.setApplySequence(this.applySequence);
|
||||
}
|
||||
if (this.ignoreSendFailures != null) {
|
||||
router.setIgnoreSendFailures(this.ignoreSendFailures);
|
||||
}
|
||||
if (this.resolutionRequired != null) {
|
||||
router.setResolutionRequired(this.resolutionRequired);
|
||||
}
|
||||
return router;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.message.MessageSource;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* FactoryBean for creating a SourcePollingChannelAdapter instance.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SourcePollingChannelAdapterFactoryBean implements FactoryBean, BeanFactoryAware, BeanNameAware,
|
||||
BeanClassLoaderAware, InitializingBean, SmartLifecycle {
|
||||
|
||||
private volatile MessageSource<?> source;
|
||||
|
||||
private volatile MessageChannel outputChannel;
|
||||
|
||||
private volatile PollerMetadata pollerMetadata;
|
||||
|
||||
private volatile boolean autoStartup = true;
|
||||
|
||||
private volatile String beanName;
|
||||
|
||||
private volatile ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private volatile ClassLoader beanClassLoader;
|
||||
|
||||
private volatile SourcePollingChannelAdapter adapter;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
|
||||
public void setSource(MessageSource<?> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public void setOutputChannel(MessageChannel outputChannel) {
|
||||
this.outputChannel = outputChannel;
|
||||
}
|
||||
|
||||
public void setPollerMetadata(PollerMetadata pollerMetadata) {
|
||||
this.pollerMetadata = pollerMetadata;
|
||||
}
|
||||
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory,
|
||||
"a ConfigurableBeanFactory is required");
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.initializeAdapter();
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
if (this.adapter == null) {
|
||||
this.initializeAdapter();
|
||||
}
|
||||
return this.adapter;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return SourcePollingChannelAdapter.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initializeAdapter() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
Assert.notNull(this.source, "source is required");
|
||||
Assert.notNull(this.outputChannel, "outputChannel is required");
|
||||
SourcePollingChannelAdapter spca = new SourcePollingChannelAdapter();
|
||||
spca.setSource(this.source);
|
||||
spca.setOutputChannel(this.outputChannel);
|
||||
if (this.pollerMetadata == null) {
|
||||
this.pollerMetadata = IntegrationContextUtils.getDefaultPollerMetadata(this.beanFactory);
|
||||
Assert.notNull(this.pollerMetadata, "No poller has been defined for channel-adapter '"
|
||||
+ this.beanName + "', and no default poller is available within the context.");
|
||||
}
|
||||
spca.setTrigger(this.pollerMetadata.getTrigger());
|
||||
spca.setMaxMessagesPerPoll(this.pollerMetadata.getMaxMessagesPerPoll());
|
||||
spca.setTaskExecutor(this.pollerMetadata.getTaskExecutor());
|
||||
spca.setTransactionManager(this.pollerMetadata.getTransactionManager());
|
||||
spca.setTransactionDefinition(this.pollerMetadata.getTransactionDefinition());
|
||||
spca.setAdviceChain(this.pollerMetadata.getAdviceChain());
|
||||
spca.setAutoStartup(this.autoStartup);
|
||||
spca.setBeanName(this.beanName);
|
||||
spca.setBeanFactory(this.beanFactory);
|
||||
spca.setBeanClassLoader(this.beanClassLoader);
|
||||
spca.afterPropertiesSet();
|
||||
this.adapter = spca;
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SmartLifecycle implementation (delegates to the created adapter)
|
||||
*/
|
||||
|
||||
public boolean isAutoStartup() {
|
||||
return (this.adapter != null) ? this.adapter.isAutoStartup() : true;
|
||||
}
|
||||
|
||||
public int getPhase() {
|
||||
return (this.adapter != null) ? this.adapter.getPhase() : 0;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return (this.adapter != null) ? this.adapter.isRunning() : false;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (this.adapter != null) {
|
||||
this.adapter.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.adapter != null) {
|
||||
this.adapter.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) {
|
||||
if (this.adapter != null) {
|
||||
this.adapter.stop(callback);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.splitter.AbstractMessageSplitter;
|
||||
import org.springframework.integration.splitter.DefaultMessageSplitter;
|
||||
import org.springframework.integration.splitter.ExpressionEvaluatingSplitter;
|
||||
import org.springframework.integration.splitter.MethodInvokingSplitter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory bean for creating a Message Splitter.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean {
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
|
||||
AbstractMessageSplitter splitter = null;
|
||||
if (targetObject instanceof AbstractMessageSplitter) {
|
||||
splitter = (AbstractMessageSplitter) targetObject;
|
||||
}
|
||||
else {
|
||||
splitter = (StringUtils.hasText(targetMethodName))
|
||||
? new MethodInvokingSplitter(targetObject, targetMethodName)
|
||||
: new MethodInvokingSplitter(targetObject);
|
||||
}
|
||||
return this.configureSplitter(splitter);
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createExpressionEvaluatingHandler(String expression) {
|
||||
return this.configureSplitter(new ExpressionEvaluatingSplitter(expression));
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createDefaultHandler() {
|
||||
return this.configureSplitter(new DefaultMessageSplitter());
|
||||
}
|
||||
|
||||
private AbstractMessageSplitter configureSplitter(AbstractMessageSplitter splitter) {
|
||||
if (this.sendTimeout != null) {
|
||||
splitter.setSendTimeout(sendTimeout);
|
||||
}
|
||||
return splitter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.transformer.ExpressionEvaluatingTransformer;
|
||||
import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.integration.transformer.MethodInvokingTransformer;
|
||||
import org.springframework.integration.transformer.Transformer;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory bean for creating a Message Transformer.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class TransformerFactoryBean extends AbstractMessageHandlerFactoryBean {
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
|
||||
Assert.notNull(targetObject, "targetObject must not be null");
|
||||
Transformer transformer = null;
|
||||
if (targetObject instanceof Transformer) {
|
||||
transformer = (Transformer) targetObject;
|
||||
}
|
||||
else if (StringUtils.hasText(targetMethodName)) {
|
||||
transformer = new MethodInvokingTransformer(targetObject, targetMethodName);
|
||||
}
|
||||
else {
|
||||
transformer = new MethodInvokingTransformer(targetObject);
|
||||
}
|
||||
return this.createHandler(transformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageHandler createExpressionEvaluatingHandler(String expression) {
|
||||
Transformer transformer = new ExpressionEvaluatingTransformer(expression);
|
||||
return this.createHandler(transformer);
|
||||
}
|
||||
|
||||
private MessageTransformingHandler createHandler(Transformer transformer) {
|
||||
MessageTransformingHandler handler = new MessageTransformingHandler(transformer);
|
||||
if (this.sendTimeout != null) {
|
||||
handler.setSendTimeout(this.sendTimeout.longValue());
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.integration.channel.BeanFactoryChannelResolver;
|
||||
import org.springframework.integration.channel.ChannelResolver;
|
||||
import org.springframework.integration.channel.SubscribableChannel;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for Method-level annotation post-processors.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation> implements MethodAnnotationPostProcessor<T> {
|
||||
|
||||
private static final String INPUT_CHANNEL_ATTRIBUTE = "inputChannel";
|
||||
|
||||
|
||||
protected final BeanFactory beanFactory;
|
||||
|
||||
protected final ChannelResolver channelResolver;
|
||||
|
||||
|
||||
public AbstractMethodAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
this.channelResolver = new BeanFactoryChannelResolver(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
public Object postProcess(Object bean, String beanName, Method method, T annotation) {
|
||||
MessageHandler handler = this.createHandler(bean, method, annotation);
|
||||
if (handler instanceof AbstractMessageHandler) {
|
||||
Order orderAnnotation = AnnotationUtils.findAnnotation(method, Order.class);
|
||||
if (orderAnnotation != null) {
|
||||
((AbstractMessageHandler) handler).setOrder(orderAnnotation.value());
|
||||
}
|
||||
}
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
handler = (MessageHandler) ((ConfigurableListableBeanFactory) beanFactory).initializeBean(handler, "_initHandlerFor_" + beanName);
|
||||
}
|
||||
AbstractEndpoint endpoint = this.createEndpoint(handler, annotation);
|
||||
if (endpoint != null) {
|
||||
return endpoint;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
protected boolean shouldCreateEndpoint(T annotation) {
|
||||
return (StringUtils.hasText((String) AnnotationUtils.getValue(annotation, INPUT_CHANNEL_ATTRIBUTE)));
|
||||
}
|
||||
|
||||
private AbstractEndpoint createEndpoint(MessageHandler handler, T annotation) {
|
||||
AbstractEndpoint endpoint = null;
|
||||
String inputChannelName = (String) AnnotationUtils.getValue(annotation, INPUT_CHANNEL_ATTRIBUTE);
|
||||
if (StringUtils.hasText(inputChannelName)) {
|
||||
MessageChannel inputChannel = this.channelResolver.resolveChannelName(inputChannelName);
|
||||
Assert.notNull(inputChannel, "failed to resolve inputChannel '" + inputChannelName + "'");
|
||||
Assert.isTrue(inputChannel instanceof SubscribableChannel,
|
||||
"The input channel for an Annotation-based endpoint must be a SubscribableChannel.");
|
||||
endpoint = new EventDrivenConsumer((SubscribableChannel) inputChannel, handler);
|
||||
if (handler instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) handler).setBeanFactory(this.beanFactory);
|
||||
}
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to create the MessageHandler.
|
||||
*/
|
||||
protected abstract MessageHandler createHandler(Object bean, Method method, T annotation);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.integration.aggregator.CorrelatingMessageHandler;
|
||||
import org.springframework.integration.aggregator.CorrelationStrategyAdapter;
|
||||
import org.springframework.integration.aggregator.MethodInvokingMessageGroupProcessor;
|
||||
import org.springframework.integration.aggregator.ReleaseStrategyAdapter;
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.CorrelationStrategy;
|
||||
import org.springframework.integration.annotation.ReleaseStrategy;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Post-processor for the {@link Aggregator @Aggregator} annotation.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class AggregatorAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Aggregator> {
|
||||
|
||||
public AggregatorAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, Aggregator annotation) {
|
||||
MethodInvokingMessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(bean, method.getName());
|
||||
ReleaseStrategyAdapter ReleaseStrategy = getReleaseStrategy(bean);
|
||||
CorrelationStrategyAdapter correlationStrategy = getCorrelationStrategy(bean);
|
||||
CorrelatingMessageHandler handler = new CorrelatingMessageHandler(processor, new SimpleMessageStore(), correlationStrategy, ReleaseStrategy);
|
||||
String discardChannelName = annotation.discardChannel();
|
||||
if (StringUtils.hasText(discardChannelName)) {
|
||||
MessageChannel discardChannel = this.channelResolver.resolveChannelName(discardChannelName);
|
||||
Assert.notNull(discardChannel, "failed to resolve discardChannel '" + discardChannelName + "'");
|
||||
handler.setDiscardChannel(discardChannel);
|
||||
}
|
||||
String outputChannelName = annotation.outputChannel();
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
handler.setOutputChannel(this.channelResolver.resolveChannelName(outputChannelName));
|
||||
}
|
||||
handler.setSendTimeout(annotation.sendTimeout());
|
||||
handler.setSendPartialResultOnExpiry(annotation.sendPartialResultsOnExpiry());
|
||||
handler.setBeanFactory(this.beanFactory);
|
||||
handler.afterPropertiesSet();
|
||||
return handler;
|
||||
}
|
||||
|
||||
private ReleaseStrategyAdapter getReleaseStrategy(final Object bean) {
|
||||
final AtomicReference<ReleaseStrategyAdapter> reference = new AtomicReference<ReleaseStrategyAdapter>();
|
||||
ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = AnnotationUtils.getAnnotation(method, ReleaseStrategy.class);
|
||||
if (annotation != null) {
|
||||
reference.set(new ReleaseStrategyAdapter(bean, method));
|
||||
}
|
||||
}
|
||||
});
|
||||
return reference.get();
|
||||
}
|
||||
|
||||
private CorrelationStrategyAdapter getCorrelationStrategy(final Object bean) {
|
||||
final AtomicReference<CorrelationStrategyAdapter> reference = new AtomicReference<CorrelationStrategyAdapter>();
|
||||
ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = AnnotationUtils.getAnnotation(method, CorrelationStrategy.class);
|
||||
if (annotation != null) {
|
||||
reference.set(new CorrelationStrategyAdapter(bean, method));
|
||||
}
|
||||
}
|
||||
});
|
||||
return reference.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.filter.MessageFilter;
|
||||
import org.springframework.integration.filter.MethodInvokingSelector;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Post-processor for Methods annotated with {@link Filter @Filter}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FilterAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Filter> {
|
||||
|
||||
public FilterAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, Filter annotation) {
|
||||
Assert.isTrue(boolean.class.equals(method.getReturnType()) || Boolean.class.equals(method.getReturnType()),
|
||||
"The Filter annotation may only be applied to methods with a boolean return type.");
|
||||
MethodInvokingSelector selector = new MethodInvokingSelector(bean, method);
|
||||
MessageFilter filter = new MessageFilter(selector);
|
||||
String outputChannelName = annotation.outputChannel();
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
filter.setOutputChannel(this.channelResolver.resolveChannelName(outputChannelName));
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
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.endpoint.AbstractEndpoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} implementation that processes method-level
|
||||
* messaging annotations such as @Transformer, @Splitter, @Router, and @Filter.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class MessagingAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean, Lifecycle, ApplicationListener {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
private final Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> postProcessors =
|
||||
new HashMap<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>>();
|
||||
|
||||
private final Set<ApplicationListener> listeners = new HashSet<ApplicationListener>();
|
||||
|
||||
private final Set<Lifecycle> lifecycles = new HashSet<Lifecycle>();
|
||||
|
||||
private volatile boolean running = true;
|
||||
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.isAssignable(ConfigurableListableBeanFactory.class, beanFactory.getClass(),
|
||||
"a ConfigurableListableBeanFactory is required");
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.beanFactory, "BeanFactory must not be null");
|
||||
postProcessors.put(Filter.class, new FilterAnnotationPostProcessor(this.beanFactory));
|
||||
postProcessors.put(Router.class, new RouterAnnotationPostProcessor(this.beanFactory));
|
||||
postProcessors.put(Transformer.class, new TransformerAnnotationPostProcessor(this.beanFactory));
|
||||
postProcessors.put(ServiceActivator.class, new ServiceActivatorAnnotationPostProcessor(this.beanFactory));
|
||||
postProcessors.put(Splitter.class, new SplitterAnnotationPostProcessor(this.beanFactory));
|
||||
postProcessors.put(Aggregator.class, new AggregatorAnnotationPostProcessor(this.beanFactory));
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
|
||||
Assert.notNull(this.beanFactory, "BeanFactory must not be null");
|
||||
final Class<?> beanClass = this.getBeanClass(bean);
|
||||
if (!this.isStereotype(beanClass)) {
|
||||
// we only post-process stereotype components
|
||||
return bean;
|
||||
}
|
||||
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
|
||||
for (Annotation annotation : annotations) {
|
||||
MethodAnnotationPostProcessor postProcessor = postProcessors.get(annotation.annotationType());
|
||||
if (postProcessor != null && shouldCreateEndpoint(annotation)) {
|
||||
Object result = postProcessor.postProcess(bean, beanName, method, annotation);
|
||||
if (result != null && result instanceof AbstractEndpoint) {
|
||||
String endpointBeanName = generateBeanName(beanName, method, annotation.annotationType());
|
||||
if (result instanceof BeanNameAware) {
|
||||
((BeanNameAware) result).setBeanName(endpointBeanName);
|
||||
}
|
||||
beanFactory.registerSingleton(endpointBeanName, result);
|
||||
if (result instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) result).setBeanFactory(beanFactory);
|
||||
}
|
||||
if (result instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) result).afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new BeanInitializationException("failed to initialize annotated component", e);
|
||||
}
|
||||
}
|
||||
if (result instanceof Lifecycle) {
|
||||
lifecycles.add((Lifecycle) result);
|
||||
if (result instanceof SmartLifecycle && ((SmartLifecycle) result).isAutoStartup()) {
|
||||
((SmartLifecycle) result).start();
|
||||
}
|
||||
}
|
||||
if (result instanceof ApplicationListener) {
|
||||
listeners.add((ApplicationListener) result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return bean;
|
||||
}
|
||||
|
||||
private boolean shouldCreateEndpoint(Annotation annotation) {
|
||||
Object inputChannel = AnnotationUtils.getValue(annotation, "inputChannel");
|
||||
return (inputChannel != null && inputChannel instanceof String
|
||||
&& StringUtils.hasText((String) inputChannel));
|
||||
}
|
||||
|
||||
private Class<?> getBeanClass(Object bean) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
return (targetClass != null) ? targetClass : bean.getClass();
|
||||
}
|
||||
|
||||
private boolean isStereotype(Class<?> beanClass) {
|
||||
List<Annotation> annotations = new ArrayList<Annotation>(Arrays.asList(beanClass.getAnnotations()));
|
||||
Class<?>[] interfaces = beanClass.getInterfaces();
|
||||
for (Class<?> iface : interfaces) {
|
||||
annotations.addAll(Arrays.asList(iface.getAnnotations()));
|
||||
}
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
if (annotationType.equals(Component.class) || annotationType.isAnnotationPresent(Component.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String generateBeanName(String originalBeanName, Method method, Class<? extends Annotation> annotationType) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "." + ClassUtils.getShortNameAsProperty(annotationType);
|
||||
String name = baseName;
|
||||
int count = 1;
|
||||
while (this.beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
for (ApplicationListener listener : listeners) {
|
||||
try {
|
||||
listener.onApplicationEvent(event);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
if (logger.isWarnEnabled() && event != null) {
|
||||
logger.warn("ApplicationEvent of type [" + event.getClass() +
|
||||
"] not accepted by ApplicationListener [" + listener + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Lifecycle implementation
|
||||
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
for (Lifecycle lifecycle : this.lifecycles) {
|
||||
if (!lifecycle.isRunning()) {
|
||||
lifecycle.start();
|
||||
}
|
||||
}
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
for (Lifecycle lifecycle : this.lifecycles) {
|
||||
if (lifecycle.isRunning()) {
|
||||
lifecycle.stop();
|
||||
}
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Strategy interface for post-processing annotated methods.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface MethodAnnotationPostProcessor<T extends Annotation> {
|
||||
|
||||
Object postProcess(Object bean, String beanName, Method method, T annotation);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.integration.annotation.Router;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.router.MethodInvokingRouter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Post-processor for Methods annotated with {@link Router @Router}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class RouterAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Router> {
|
||||
|
||||
public RouterAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, Router annotation) {
|
||||
MethodInvokingRouter router = new MethodInvokingRouter(bean, method);
|
||||
router.setChannelResolver(this.channelResolver);
|
||||
String defaultOutputChannelName = annotation.defaultOutputChannel();
|
||||
if (StringUtils.hasText(defaultOutputChannelName)) {
|
||||
MessageChannel defaultOutputChannel = this.channelResolver.resolveChannelName(defaultOutputChannelName);
|
||||
Assert.notNull(defaultOutputChannel, "unable to resolve defaultOutputChannel '" + defaultOutputChannelName + "'");
|
||||
router.setDefaultOutputChannel(defaultOutputChannel);
|
||||
}
|
||||
return router;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.handler.ServiceActivatingHandler;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Post-processor for Methods annotated with {@link ServiceActivator @ServiceActivator}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ServiceActivatorAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<ServiceActivator> {
|
||||
|
||||
public ServiceActivatorAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, ServiceActivator annotation) {
|
||||
ServiceActivatingHandler serviceActivator = new ServiceActivatingHandler(bean, method);
|
||||
String outputChannelName = annotation.outputChannel();
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
serviceActivator.setOutputChannel(this.channelResolver.resolveChannelName(outputChannelName));
|
||||
}
|
||||
return serviceActivator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.integration.annotation.Splitter;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.splitter.MethodInvokingSplitter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Post-processor for Methods annotated with {@link Splitter @Splitter}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SplitterAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Splitter> {
|
||||
|
||||
public SplitterAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, Splitter annotation) {
|
||||
MethodInvokingSplitter splitter = new MethodInvokingSplitter(bean, method);
|
||||
String outputChannelName = annotation.outputChannel();
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
splitter.setOutputChannel(this.channelResolver.resolveChannelName(outputChannelName));
|
||||
}
|
||||
return splitter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.integration.annotation.Transformer;
|
||||
import org.springframework.integration.message.MessageHandler;
|
||||
import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.integration.transformer.MethodInvokingTransformer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Post-processor for Methods annotated with {@link Transformer @Transformer}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class TransformerAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Transformer> {
|
||||
|
||||
public TransformerAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, Transformer annotation) {
|
||||
MethodInvokingTransformer transformer = new MethodInvokingTransformer(bean, method);
|
||||
MessageTransformingHandler handler = new MessageTransformingHandler(transformer);
|
||||
String outputChannelName = annotation.outputChannel();
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
handler.setOutputChannel(this.channelResolver.resolveChannelName(outputChannelName));
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base parser for Channel Adapters.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected final String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
|
||||
String id = element.getAttribute("id");
|
||||
if (!element.hasAttribute("channel")) {
|
||||
// the created channel will get the 'id', so the adapter's bean name includes a suffix
|
||||
id = id + ".adapter";
|
||||
}
|
||||
else if (!StringUtils.hasText(id)) {
|
||||
id = parserContext.getReaderContext().generateBeanName(definition);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
String channelName = element.getAttribute("channel");
|
||||
if (!StringUtils.hasText(channelName)) {
|
||||
channelName = this.createDirectChannel(element, parserContext);
|
||||
}
|
||||
return doParse(element, parserContext, channelName);
|
||||
}
|
||||
|
||||
private String createDirectChannel(Element element, ParserContext parserContext) {
|
||||
String channelId = element.getAttribute("id");
|
||||
if (!StringUtils.hasText(channelId)) {
|
||||
parserContext.getReaderContext().error("The channel-adapter's 'id' attribute is required when no 'channel' "
|
||||
+ "reference has been provided, because that 'id' would be used for the created channel.", element);
|
||||
}
|
||||
BeanDefinitionBuilder channelBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.DirectChannel");
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(channelBuilder.getBeanDefinition(), channelId);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
|
||||
return channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to parse the adapter element.
|
||||
* The name of the MessageChannel bean is provided.
|
||||
*/
|
||||
protected abstract AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 java.util.List;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Base parser for routers that create instances that are subclasses of AbstractChannelNameResolvingMessageRouter.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractChannelNameResolvingRouterParser extends AbstractRouterParser {
|
||||
|
||||
@Override
|
||||
protected final BeanDefinition parseRouter(Element element, ParserContext parserContext) {
|
||||
BeanDefinition beanDefinition = this.doParseRouter(element, parserContext);
|
||||
if (beanDefinition != null) {
|
||||
// check if mapping is provided otherwise returned values will be treated as channel names
|
||||
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "mapping");
|
||||
if (childElements != null && childElements.size() > 0) {
|
||||
BeanDefinitionBuilder channelResolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.MapBasedChannelResolver");
|
||||
ManagedMap<String, RuntimeBeanReference> channelMap = new ManagedMap<String, RuntimeBeanReference>();
|
||||
for (Element childElement : childElements) {
|
||||
channelMap.put(childElement.getAttribute("value"),
|
||||
new RuntimeBeanReference(childElement.getAttribute("channel")));
|
||||
}
|
||||
channelResolverBuilder.addPropertyValue("channelMap", channelMap);
|
||||
beanDefinition.getPropertyValues().add("channelResolver", channelResolverBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
protected abstract BeanDefinition doParseRouter(Element element, ParserContext parserContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Base class for channel parsers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractChannelParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = this.buildBeanDefinition(element, parserContext);
|
||||
ManagedList interceptors = null;
|
||||
Element interceptorsElement = DomUtils.getChildElementByTagName(element, "interceptors");
|
||||
if (interceptorsElement != null) {
|
||||
ChannelInterceptorParser interceptorParser = new ChannelInterceptorParser();
|
||||
interceptors = interceptorParser.parseInterceptors(interceptorsElement, parserContext);
|
||||
}
|
||||
if (interceptors == null) {
|
||||
interceptors = new ManagedList();
|
||||
}
|
||||
String datatypeAttr = element.getAttribute("datatype");
|
||||
if (StringUtils.hasText(datatypeAttr)) {
|
||||
// TODO: remove this once the editor fallback is working (3.0 GA)
|
||||
// it should be replaced with: builder.addPropertyValue("datatypes", datatypeAttr);
|
||||
String[] classnames = StringUtils.commaDelimitedListToStringArray(datatypeAttr);
|
||||
Class<?>[] datatypes = new Class<?>[classnames.length];
|
||||
int i = 0;
|
||||
for (String classname : classnames) {
|
||||
datatypes[i++] = ClassUtils.resolveClassName(classname.trim(), this.getClass().getClassLoader());
|
||||
}
|
||||
builder.addPropertyValue("datatypes", datatypes);
|
||||
}
|
||||
builder.addPropertyValue("interceptors", interceptors);
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to create the bean definition.
|
||||
* The class must be defined, and any implementation-specific constructor
|
||||
* arguments or properties should be configured. This base class will
|
||||
* configure the interceptors including the 'datatype' interceptor if
|
||||
* the 'datatype' attribute is defined on the channel element.
|
||||
*/
|
||||
protected abstract BeanDefinitionBuilder buildBeanDefinition(Element element, ParserContext parserContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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 java.util.List;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Base class parser for elements that create Message Endpoints.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
protected static final String REF_ATTRIBUTE = "ref";
|
||||
|
||||
protected static final String METHOD_ATTRIBUTE = "method";
|
||||
|
||||
protected static final String EXPRESSION_ATTRIBUTE = "expression";
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateIdAsFallback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the MessageHandler.
|
||||
*/
|
||||
protected abstract BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext);
|
||||
|
||||
protected String getInputChannelAttributeName() {
|
||||
return "input-channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder handlerBuilder = this.parseHandler(element, parserContext);
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(handlerBuilder, element, "output-channel");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "order");
|
||||
AbstractBeanDefinition handlerBeanDefinition = handlerBuilder.getBeanDefinition();
|
||||
String inputChannelAttributeName = this.getInputChannelAttributeName();
|
||||
if (!element.hasAttribute(inputChannelAttributeName)) {
|
||||
if (!parserContext.isNested()) {
|
||||
String elementDescription = IntegrationNamespaceUtils.createElementDescription(element);
|
||||
parserContext.getReaderContext().error("The '" + inputChannelAttributeName
|
||||
+ "' attribute is required for the top-level endpoint element "
|
||||
+ elementDescription + ".", element);
|
||||
}
|
||||
return handlerBeanDefinition;
|
||||
}
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.ConsumerEndpointFactoryBean");
|
||||
String handlerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(handlerBeanDefinition, parserContext.getRegistry());
|
||||
builder.addPropertyReference("handler", handlerBeanName);
|
||||
String inputChannelName = element.getAttribute(inputChannelAttributeName);
|
||||
boolean channelExists = false;
|
||||
if (parserContext.getRegistry() instanceof BeanFactory) {
|
||||
// BeanFactory also checks ancestor contexts in a hierarchy
|
||||
channelExists = ((BeanFactory) parserContext.getRegistry()).containsBean(inputChannelName);
|
||||
}
|
||||
else {
|
||||
channelExists = parserContext.getRegistry().containsBeanDefinition(inputChannelName);
|
||||
}
|
||||
if (!channelExists) {
|
||||
// create a default DirectChannel instance
|
||||
BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.DirectChannel");
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), inputChannelName);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
|
||||
}
|
||||
builder.addPropertyValue("inputChannelName", inputChannelName);
|
||||
List<Element> pollerElementList = DomUtils.getChildElementsByTagName(element, "poller");
|
||||
if (!CollectionUtils.isEmpty(pollerElementList)) {
|
||||
if (pollerElementList.size() != 1) {
|
||||
parserContext.getReaderContext().error(
|
||||
"at most one poller element may be configured for an endpoint", element);
|
||||
}
|
||||
IntegrationNamespaceUtils.configurePollerMetadata(pollerElementList.get(0), builder, parserContext);
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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 org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Base parser class for endpoints that delegate to a method invoker or
|
||||
* expression evaluator when handling consumed Messages. These classes
|
||||
* use a FactoryBean implementation to construct the actual endpoint
|
||||
* instance.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
abstract class AbstractDelegatingConsumerEndpointParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected final BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(this.getFactoryBeanClassName());
|
||||
BeanComponentDefinition innerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
|
||||
String ref = element.getAttribute(REF_ATTRIBUTE);
|
||||
String expression = element.getAttribute(EXPRESSION_ATTRIBUTE);
|
||||
boolean hasRef = StringUtils.hasText(ref);
|
||||
boolean hasExpression = StringUtils.hasText(expression);
|
||||
if (innerDefinition != null) {
|
||||
if (hasRef || hasExpression) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Neither 'ref' nor 'expression' are permitted when an inner bean (<bean/>) is configured.", element);
|
||||
return null;
|
||||
}
|
||||
builder.addPropertyValue("targetObject", innerDefinition);
|
||||
}
|
||||
else if (hasRef) {
|
||||
builder.addPropertyReference("targetObject", ref);
|
||||
}
|
||||
else if (hasExpression) {
|
||||
builder.addPropertyValue("expression", expression);
|
||||
}
|
||||
else if (!this.hasDefaultOption()) {
|
||||
parserContext.getReaderContext().error("Exactly one of the 'ref' attribute, 'expression' attribute, " +
|
||||
"or inner bean (<bean/>) definition is required for this '" + element.getLocalName() + "' endpoint.",
|
||||
element);
|
||||
return null;
|
||||
}
|
||||
String method = element.getAttribute(METHOD_ATTRIBUTE);
|
||||
if (StringUtils.hasText(method)) {
|
||||
if (hasExpression) {
|
||||
parserContext.getReaderContext().error(
|
||||
"A 'method' attribute is not permitted when configuring an 'expression'.", element);
|
||||
}
|
||||
if (hasRef || innerDefinition != null) {
|
||||
builder.addPropertyValue("targetMethodName", method);
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error("A 'method' attribute is only permitted when either " +
|
||||
"a 'ref' or inner-bean definition is provided.", element);
|
||||
}
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout");
|
||||
this.postProcess(builder, element, parserContext);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this no-op method to provide additional configuration.
|
||||
*/
|
||||
void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
|
||||
}
|
||||
|
||||
abstract boolean hasDefaultOption();
|
||||
|
||||
abstract String getFactoryBeanClassName();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for inbound gateway parsers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractInboundGatewayParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
String id = super.resolveId(element, definition, parserContext);
|
||||
if (!StringUtils.hasText(id)) {
|
||||
id = element.getAttribute("name");
|
||||
}
|
||||
if (!StringUtils.hasText(id)) {
|
||||
id = parserContext.getReaderContext().generateBeanName(definition);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return !attributeName.equals("name") && !attributeName.equals("request-channel")
|
||||
&& !attributeName.equals("reply-channel") && super.isEligibleAttribute(attributeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void postProcess(BeanDefinitionBuilder builder, Element element) {
|
||||
String requestChannelRef = element.getAttribute("request-channel");
|
||||
Assert.hasText(requestChannelRef, "a 'request-channel' reference is required");
|
||||
builder.addPropertyReference("requestChannel", requestChannelRef);
|
||||
String replyChannel = element.getAttribute("reply-channel");
|
||||
if (StringUtils.hasText(replyChannel)) {
|
||||
builder.addPropertyReference("replyChannel", replyChannel);
|
||||
}
|
||||
String autoStartup = element.getAttribute("auto-startup");
|
||||
if (StringUtils.hasText(autoStartup)) {
|
||||
builder.addPropertyValue("autoStartup", autoStartup);
|
||||
}
|
||||
this.doPostProcess(builder, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may add to the bean definition by overriding this method.
|
||||
*/
|
||||
protected void doPostProcess(BeanDefinitionBuilder builder, Element element) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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 org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
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.xml.BeanDefinitionDecorator;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandler;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Base class for NamespaceHandlers that registers a BeanFactoryPostProcessor
|
||||
* for configuring default bean definitions.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHandler {
|
||||
|
||||
private static final String DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME =
|
||||
"DefaultConfiguringBeanFactoryPostProcessor";
|
||||
|
||||
private static final String DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME =
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".internal" + DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME;
|
||||
|
||||
|
||||
private final NamespaceHandlerDelegate delegate = new NamespaceHandlerDelegate();
|
||||
|
||||
|
||||
public final BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
this.registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(parserContext);
|
||||
return this.delegate.parse(element, parserContext);
|
||||
}
|
||||
|
||||
public final BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext) {
|
||||
return this.delegate.decorate(source, definition, parserContext);
|
||||
}
|
||||
|
||||
private void registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(ParserContext parserContext) {
|
||||
boolean alreadyRegistered = false;
|
||||
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
|
||||
alreadyRegistered = ObjectUtils.containsElement(
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) parserContext.getRegistry(),
|
||||
DefaultConfiguringBeanFactoryPostProcessor.class, false, false),
|
||||
DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
|
||||
}
|
||||
else {
|
||||
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
|
||||
}
|
||||
if (!alreadyRegistered) {
|
||||
BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml." + DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME);
|
||||
BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder(
|
||||
postProcessorBuilder.getBeanDefinition(), DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(postProcessorHolder, parserContext.getRegistry());
|
||||
}
|
||||
}
|
||||
|
||||
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator decorator) {
|
||||
this.delegate.doRegisterBeanDefinitionDecorator(elementName, decorator);
|
||||
}
|
||||
|
||||
protected final void registerBeanDefinitionDecoratorForAttribute(String attributeName, BeanDefinitionDecorator decorator) {
|
||||
this.delegate.doRegisterBeanDefinitionDecoratorForAttribute(attributeName, decorator);
|
||||
}
|
||||
|
||||
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
|
||||
this.delegate.doRegisterBeanDefinitionParser(elementName, parser);
|
||||
}
|
||||
|
||||
|
||||
private class NamespaceHandlerDelegate extends NamespaceHandlerSupport {
|
||||
|
||||
public void init() {
|
||||
AbstractIntegrationNamespaceHandler.this.init();
|
||||
}
|
||||
|
||||
private void doRegisterBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator decorator) {
|
||||
super.registerBeanDefinitionDecorator(elementName, decorator);
|
||||
}
|
||||
|
||||
private void doRegisterBeanDefinitionDecoratorForAttribute(String attributeName, BeanDefinitionDecorator decorator) {
|
||||
super.registerBeanDefinitionDecoratorForAttribute(attributeName, decorator);
|
||||
}
|
||||
|
||||
private void doRegisterBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
|
||||
super.registerBeanDefinitionParser(elementName, parser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Base class for outbound Channel Adapter parsers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractOutboundChannelAdapterParser extends AbstractChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) {
|
||||
Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.ConsumerEndpointFactoryBean");
|
||||
builder.addPropertyReference("handler", this.parseAndRegisterConsumer(element, parserContext));
|
||||
if (pollerElement != null) {
|
||||
if (!StringUtils.hasText(channelName)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"outbound channel adapter with a 'poller' requires a 'channel' to poll", element);
|
||||
}
|
||||
IntegrationNamespaceUtils.configurePollerMetadata(pollerElement, builder, parserContext);
|
||||
}
|
||||
builder.addPropertyValue("inputChannelName", channelName);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to control the registration process and return the bean name.
|
||||
* If parsing a bean definition whose name can be auto-generated, consider using
|
||||
* {@link #parseConsumer(Element, ParserContext)} instead.
|
||||
*/
|
||||
protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) {
|
||||
AbstractBeanDefinition definition = this.parseConsumer(element, parserContext);
|
||||
if (definition == null) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Consumer parsing must return a BeanComponentDefinition.", element);
|
||||
}
|
||||
String order = element.getAttribute("order");
|
||||
if (StringUtils.hasText(order)) {
|
||||
definition.getPropertyValues().addPropertyValue("order", order);
|
||||
}
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
definition, parserContext.getRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to return the BeanDefinition for the MessageConsumer. It will
|
||||
* be registered with a generated name.
|
||||
*/
|
||||
protected abstract AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for url-based outbound gateway parsers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
protected abstract String getGatewayClassName(Element element);
|
||||
|
||||
@Override
|
||||
protected String getInputChannelAttributeName() {
|
||||
return "request-channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
String id = super.resolveId(element, definition, parserContext);
|
||||
if (!StringUtils.hasText(id)) {
|
||||
id = element.getAttribute("name");
|
||||
}
|
||||
if (!StringUtils.hasText(id)) {
|
||||
id = parserContext.getReaderContext().generateBeanName(definition);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(this.getGatewayClassName(element));
|
||||
String url = this.parseUrl(element, parserContext);
|
||||
builder.addConstructorArgValue(url);
|
||||
String replyChannel = element.getAttribute("reply-channel");
|
||||
if (StringUtils.hasText(replyChannel)) {
|
||||
builder.addPropertyReference("replyChannel", replyChannel);
|
||||
}
|
||||
this.postProcessGateway(builder, element, parserContext);
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected String parseUrl(Element element, ParserContext parserContext) {
|
||||
String url = element.getAttribute("url");
|
||||
if (!StringUtils.hasText(url)) {
|
||||
parserContext.getReaderContext().error("The 'url' attribute is required.", element);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this method for additional configuration.
|
||||
*/
|
||||
protected void postProcessGateway(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Base parser for inbound Channel Adapters that poll a source.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractPollingInboundChannelAdapterParser extends AbstractChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) {
|
||||
String source = this.parseSource(element, parserContext);
|
||||
if (!StringUtils.hasText(source)) {
|
||||
parserContext.getReaderContext().error("failed to parse source", element);
|
||||
}
|
||||
BeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SourcePollingChannelAdapterFactoryBean");
|
||||
adapterBuilder.addPropertyReference("source", source);
|
||||
adapterBuilder.addPropertyReference("outputChannel", channelName);
|
||||
Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
|
||||
if (pollerElement != null) {
|
||||
IntegrationNamespaceUtils.configurePollerMetadata(pollerElement, adapterBuilder, parserContext);
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "auto-startup");
|
||||
return adapterBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to parse the PollableSource instance
|
||||
* which the created Channel Adapter will poll.
|
||||
*/
|
||||
protected abstract String parseSource(Element element, ParserContext parserContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* Base parser for routers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractRouterParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected final BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.RouterFactoryBean");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "default-output-channel");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "timeout");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "resolution-required");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "apply-sequence");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-send-failures");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-channel-name-resolution-failures");
|
||||
BeanDefinition targetRouterBeanDefinition = this.parseRouter(element, parserContext);
|
||||
builder.addPropertyValue("targetObject", targetRouterBeanDefinition);
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected abstract BeanDefinition parseRouter(Element element, ParserContext parserContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractTransformerParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.MessageTransformingHandler");
|
||||
BeanDefinitionBuilder transformerBuilder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(this.getTransformerClassName());
|
||||
this.parseTransformer(element, parserContext, transformerBuilder);
|
||||
String transformerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
transformerBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
builder.addConstructorArgReference(transformerBeanName);
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected abstract String getTransformerClassName();
|
||||
|
||||
protected abstract void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for the <em>aggregator</em> element of the integration namespace. Registers the annotation-driven
|
||||
* post-processors.
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class AggregatorParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
private static final String RELEASE_STRATEGY_REF_ATTRIBUTE = "release-strategy";
|
||||
|
||||
private static final String RELEASE_STRATEGY_METHOD_ATTRIBUTE = "release-strategy-method";
|
||||
|
||||
private static final String CORRELATION_STRATEGY_REF_ATTRIBUTE = "correlation-strategy";
|
||||
|
||||
private static final String CORRELATION_STRATEGY_METHOD_ATTRIBUTE = "correlation-strategy-method";
|
||||
|
||||
private static final String MESSAGE_STORE_ATTRIBUTE = "message-store";
|
||||
|
||||
private static final String OUTPUT_CHANNEL_ATTRIBUTE = "output-channel";
|
||||
|
||||
private static final String DISCARD_CHANNEL_ATTRIBUTE = "discard-channel";
|
||||
|
||||
private static final String SEND_TIMEOUT_ATTRIBUTE = "send-timeout";
|
||||
|
||||
private static final String SEND_PARTIAL_RESULT_ON_EXPIRY_ATTRIBUTE = "send-partial-result-on-expiry";
|
||||
|
||||
private static final String RELEASE_STRATEGY_PROPERTY = "releaseStrategy";
|
||||
|
||||
private static final String CORRELATION_STRATEGY_PROPERTY = "correlationStrategy";
|
||||
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanComponentDefinition innerHandlerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
|
||||
String ref = element.getAttribute(REF_ATTRIBUTE);
|
||||
BeanDefinitionBuilder builder;
|
||||
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".aggregator.CorrelatingMessageHandler");
|
||||
BeanDefinitionBuilder processorBuilder = null;
|
||||
|
||||
if (innerHandlerDefinition != null || StringUtils.hasText(ref)) {
|
||||
processorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".aggregator.MethodInvokingMessageGroupProcessor");
|
||||
builder.addConstructorArgValue(processorBuilder.getBeanDefinition());
|
||||
} else {
|
||||
builder.addConstructorArgValue(BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".aggregator.DefaultAggregatingMessageGroupProcessor").getBeanDefinition());
|
||||
}
|
||||
|
||||
if (innerHandlerDefinition != null) {
|
||||
processorBuilder.addConstructorArgValue(innerHandlerDefinition);
|
||||
} else {
|
||||
if (StringUtils.hasText(ref)) {
|
||||
processorBuilder.addConstructorArgReference(ref);
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) {
|
||||
String method = element.getAttribute(METHOD_ATTRIBUTE);
|
||||
processorBuilder.getRawBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(method, "java.lang.String");
|
||||
}
|
||||
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,
|
||||
MESSAGE_STORE_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,
|
||||
DISCARD_CHANNEL_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,
|
||||
OUTPUT_CHANNEL_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
|
||||
SEND_TIMEOUT_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
|
||||
SEND_PARTIAL_RESULT_ON_EXPIRY_ATTRIBUTE);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
|
||||
this.injectPropertyWithBean(RELEASE_STRATEGY_REF_ATTRIBUTE,
|
||||
RELEASE_STRATEGY_METHOD_ATTRIBUTE, RELEASE_STRATEGY_PROPERTY,
|
||||
"ReleaseStrategyAdapter", element, builder, parserContext);
|
||||
this.injectPropertyWithBean(CORRELATION_STRATEGY_REF_ATTRIBUTE,
|
||||
CORRELATION_STRATEGY_METHOD_ATTRIBUTE, CORRELATION_STRATEGY_PROPERTY,
|
||||
"CorrelationStrategyAdapter", element, builder, parserContext);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void injectPropertyWithBean(String beanRefAttribute, String methodRefAttribute,
|
||||
String beanProperty, String adapterClass, Element element,
|
||||
BeanDefinitionBuilder builder, ParserContext parserContext) {
|
||||
final String beanRef = element.getAttribute(beanRefAttribute);
|
||||
final String beanMethod = element.getAttribute(methodRefAttribute);
|
||||
if (StringUtils.hasText(beanRef)) {
|
||||
if (StringUtils.hasText(beanMethod)) {
|
||||
String adapterBeanName = this.createAdapter(beanRef, beanMethod, adapterClass,
|
||||
parserContext);
|
||||
builder.addPropertyReference(beanProperty, adapterBeanName);
|
||||
} else {
|
||||
builder.addPropertyReference(beanProperty, beanRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String createAdapter(String ref, String method, String unqualifiedClassName,
|
||||
ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".aggregator." + unqualifiedClassName);
|
||||
builder.addConstructorArgReference(ref);
|
||||
builder.getRawBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(method, "java.lang.String");
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(),
|
||||
parserContext.getRegistry());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* Parser for the <annotation-config> element of the integration namespace.
|
||||
* Adds a {@link org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor}
|
||||
* to the application context.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class AnnotationConfigParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private final static String PACKAGE_NAME = IntegrationNamespaceUtils.BASE_PACKAGE + ".config.annotation";
|
||||
|
||||
|
||||
@Override
|
||||
protected String getBeanClassName(Element element) {
|
||||
return PACKAGE_NAME + ".MessagingAnnotationPostProcessor";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
return PACKAGE_NAME + ".internalMessagingAnnotationPostProcessor";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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 java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.core.SpringVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <application-event-multicaster> element of the
|
||||
* integration namespace.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ApplicationEventMulticasterParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected String getBeanClassName(Element element) {
|
||||
return "org.springframework.context.event.SimpleApplicationEventMulticaster";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
return AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String taskExecutorRef = element.getAttribute("task-executor");
|
||||
if (StringUtils.hasText(taskExecutorRef)) {
|
||||
builder.addPropertyReference("taskExecutor", taskExecutorRef);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder executorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor");
|
||||
executorBuilder.addPropertyValue("corePoolSize", 1);
|
||||
executorBuilder.addPropertyValue("maxPoolSize", 10);
|
||||
executorBuilder.addPropertyValue("queueCapacity", 0);
|
||||
executorBuilder.addPropertyValue("threadNamePrefix", "event-multicaster-");
|
||||
String executorBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
executorBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
builder.addPropertyReference("taskExecutor", executorBeanName);
|
||||
}
|
||||
String springVersion = SpringVersion.getVersion();
|
||||
if (springVersion != null && springVersion.startsWith("2")) {
|
||||
builder.addPropertyValue("collectionClass", CopyOnWriteArraySet.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* Simple strategy interface for parsers that are responsible
|
||||
* for parsing an element, creating a bean definition, and then
|
||||
* registering the bean. The {@link #parse(Element, ParserContext)}
|
||||
* method should return the name of the registered bean.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface BeanDefinitionRegisteringParser {
|
||||
|
||||
String parse(Element element, ParserContext parserContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* Parser for the <bridge> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class BridgeParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
return BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".handler.BridgeHandler");
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user