Removed old style channel adapters and refactored basic JMS source and target for new style.

This commit is contained in:
Mark Fisher
2007-12-28 22:32:19 +00:00
parent 618e228392
commit 5dccbf7516
9 changed files with 163 additions and 472 deletions

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2002-2007 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.adapter.jms;
import java.util.Arrays;
import java.util.Collection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.MessagingConfigurationException;
import org.springframework.integration.adapter.PollableSource;
import org.springframework.jms.core.JmsTemplate;
/**
* A source for receiving JMS Messages with a polling listener. This source is
* only recommended for very low message volume. Otherwise, an event-driven
* option that uses one of Spring's MessageListener containers is highly
* recommended.
*
* @author Mark Fisher
*/
public class JmsPollableSource implements PollableSource<Object>, InitializingBean {
private ConnectionFactory connectionFactory;
private Destination destination;
private JmsTemplate jmsTemplate;
public JmsPollableSource(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public JmsPollableSource(ConnectionFactory connectionFactory, Destination destination) {
this.connectionFactory = connectionFactory;
this.destination = destination;
this.initJmsTemplate();
}
/**
* No-arg constructor provided for convenience when configuring with
* setters. Note that the initialization callback will validate.
*/
public JmsPollableSource() {
}
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void afterPropertiesSet() {
if (this.jmsTemplate == null) {
if (this.connectionFactory == null || this.destination == null) {
throw new MessagingConfigurationException("Either a 'jmsTemplate' or "
+ "*both* 'connectionFactory' and 'destination' are required.");
}
this.initJmsTemplate();
}
}
private void initJmsTemplate() {
this.jmsTemplate = new JmsTemplate();
this.jmsTemplate.setConnectionFactory(this.connectionFactory);
this.jmsTemplate.setDefaultDestination(this.destination);
}
public Collection<Object> poll(int limit) {
return Arrays.asList(this.jmsTemplate.receiveAndConvert());
}
}

View File

@@ -14,25 +14,27 @@
* limitations under the License.
*/
package org.springframework.integration.endpoint;
package org.springframework.integration.adapter.jms;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import org.springframework.integration.adapter.PollingSourceAdapter;
import org.springframework.jms.core.JmsTemplate;
/**
* A convenience base class for outbound channel adapters.
* A convenience adapter that wraps a {@link JmsPollableSource}.
*
* @author Mark Fisher
*/
public abstract class AbstractOutboundChannelAdapter extends AbstractChannelAdapter {
public class JmsPollingSourceAdapter extends PollingSourceAdapter<Object> {
@Override
protected Object receiveObject() throws Exception {
return null;
public JmsPollingSourceAdapter(ConnectionFactory connectionFactory, Destination destination) {
super(new JmsPollableSource(connectionFactory, destination));
}
@Override
protected boolean sendObject(Object object) throws Exception {
return this.doSendObject(object);
public JmsPollingSourceAdapter(JmsTemplate jmsTemplate) {
super(new JmsPollableSource(jmsTemplate));
}
protected abstract boolean doSendObject(Object object) throws Exception;
}

View File

@@ -14,21 +14,22 @@
* limitations under the License.
*/
package org.springframework.integration.endpoint.jms;
package org.springframework.integration.adapter.jms;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.MessagingConfigurationException;
import org.springframework.integration.endpoint.AbstractOutboundChannelAdapter;
import org.springframework.integration.adapter.Target;
import org.springframework.jms.core.JmsTemplate;
/**
* A simple channel adapter for sending JMS Messages.
* A target for sending JMS Messages.
*
* @author Mark Fisher
*/
public class JmsOutboundAdapter extends AbstractOutboundChannelAdapter {
public class JmsTarget implements Target<Object>, InitializingBean {
private ConnectionFactory connectionFactory;
@@ -37,6 +38,23 @@ public class JmsOutboundAdapter extends AbstractOutboundChannelAdapter {
private JmsTemplate jmsTemplate;
public JmsTarget(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public JmsTarget(ConnectionFactory connectionFactory, Destination destination) {
this.connectionFactory = connectionFactory;
this.destination = destination;
this.initJmsTemplate();
}
/**
* No-arg constructor provided for convenience when configuring with
* setters. Note that the initialization callback will validate.
*/
public JmsTarget() {
}
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
@@ -49,21 +67,23 @@ public class JmsOutboundAdapter extends AbstractOutboundChannelAdapter {
this.jmsTemplate = jmsTemplate;
}
@Override
public void initialize() {
public void afterPropertiesSet() {
if (this.jmsTemplate == null) {
if (this.connectionFactory == null || this.destination == null) {
throw new MessagingConfigurationException("Either a 'jmsTemplate' or " +
"*both* 'connectionFactory' and 'destination' are required.");
}
this.jmsTemplate = new JmsTemplate();
this.jmsTemplate.setConnectionFactory(this.connectionFactory);
this.jmsTemplate.setDefaultDestination(this.destination);
this.initJmsTemplate();
}
}
@Override
protected boolean doSendObject(Object object) throws Exception {
private void initJmsTemplate() {
this.jmsTemplate = new JmsTemplate();
this.jmsTemplate.setConnectionFactory(this.connectionFactory);
this.jmsTemplate.setDefaultDestination(this.destination);
}
public boolean send(Object object) {
this.jmsTemplate.convertAndSend(object);
return true;
}

View File

@@ -14,23 +14,27 @@
* limitations under the License.
*/
package org.springframework.integration.endpoint;
package org.springframework.integration.adapter.jms;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import org.springframework.integration.adapter.DefaultTargetAdapter;
import org.springframework.jms.core.JmsTemplate;
/**
* Convenience base class for inbound channel adapters.
* A convenience adapter that wraps a {@link JmsTarget}.
*
* @author Mark Fisher
*/
public abstract class AbstractInboundChannelAdapter extends AbstractChannelAdapter {
public class JmsTargetAdapter extends DefaultTargetAdapter {
protected boolean sendObject(Object object) throws Exception {
return false;
public JmsTargetAdapter(ConnectionFactory connectionFactory, Destination destination) {
super(new JmsTarget(connectionFactory, destination));
}
protected Object receiveObject() throws Exception {
return this.doReceiveObject();
public JmsTargetAdapter(JmsTemplate jmsTemplate) {
super(new JmsTarget(jmsTemplate));
}
protected abstract Object doReceiveObject() throws Exception;
}

View File

@@ -33,7 +33,9 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.OrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.MessagingConfigurationException;
import org.springframework.integration.adapter.DefaultTargetAdapter;
import org.springframework.integration.adapter.MethodInvokingSource;
import org.springframework.integration.adapter.MethodInvokingTarget;
import org.springframework.integration.adapter.PollingSourceAdapter;
import org.springframework.integration.annotation.DefaultOutput;
import org.springframework.integration.annotation.Handler;
@@ -47,7 +49,6 @@ import org.springframework.integration.channel.ChannelRegistryAware;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.channel.PointToPointChannel;
import org.springframework.integration.endpoint.GenericMessageEndpoint;
import org.springframework.integration.endpoint.OutboundMethodInvokingChannelAdapter;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.handler.MessageHandlerChain;
import org.springframework.integration.handler.config.DefaultMessageHandlerCreator;
@@ -166,12 +167,15 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
if (foundDefaultOutput) {
throw new MessagingConfigurationException("only one @DefaultOutput allowed per endpoint");
}
OutboundMethodInvokingChannelAdapter<Object> adapter = new OutboundMethodInvokingChannelAdapter<Object>();
adapter.setObject(bean);
adapter.setMethod(method.getName());
adapter.afterPropertiesSet();
MethodInvokingTarget<Object> target = new MethodInvokingTarget<Object>();
target.setObject(bean);
target.setMethod(method.getName());
target.afterPropertiesSet();
DefaultTargetAdapter adapter = new DefaultTargetAdapter(target);
PointToPointChannel channel = new PointToPointChannel();
String channelName = beanName + "-defaultOutputChannel";
messageBus.registerChannel(channelName, adapter);
messageBus.registerChannel(channelName, channel);
messageBus.registerTargetAdapter(beanName + "-targetAdapter", adapter);
endpoint.setDefaultOutputChannelName(channelName);
foundDefaultOutput = true;
return;

View File

@@ -1,181 +0,0 @@
/*
* Copyright 2002-2007 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.endpoint;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageMapper;
import org.springframework.integration.message.SimplePayloadMessageMapper;
import org.springframework.util.Assert;
/**
* Convenience base class for channel adapters.
*
* @author Mark Fisher
*/
public abstract class AbstractChannelAdapter implements MessageChannel, InitializingBean, BeanNameAware {
protected Log logger = LogFactory.getLog(this.getClass());
private String name;
private MessageMapper mapper = new SimplePayloadMessageMapper();
private volatile boolean initialized;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setBeanName(String beanName) {
this.setName(beanName);
}
public final void afterPropertiesSet() {
this.initialize();
this.initialized = true;
}
public void setMapper(MessageMapper mapper) {
Assert.notNull(mapper, "'mapper' must not be null");
this.mapper = mapper;
}
protected MessageMapper getMapper() {
return this.mapper;
}
public boolean send(Message message) {
if (!this.initialized) {
throw new MessageHandlingException("adapter not initialized");
}
try {
Object source = this.getMapper().fromMessage(message);
return this.sendObject(source);
}
catch (Exception e) {
throw new MessageHandlingException("failed to send message", e);
}
}
public boolean send(final Message message, long timeout) {
if (!this.initialized) {
throw new MessageHandlingException("adapter not initialized");
}
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Boolean> result = executor.submit(new Callable<Boolean>() {
public Boolean call() throws Exception {
return send(message);
}
});
try {
result.get(timeout, TimeUnit.MILLISECONDS);
if (result.isDone()) {
return result.get();
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
catch (TimeoutException e) {
return false;
}
catch (ExecutionException e) {
throw new MessageHandlingException("Exception occurred in message source", e);
}
result.cancel(true);
return false;
}
public Message receive() {
if (!this.initialized) {
throw new MessageHandlingException("adapter not initialized");
}
try {
Object result = this.receiveObject();
if (result != null) {
return this.getMapper().toMessage(result);
}
}
catch (Exception e) {
throw new MessageHandlingException("Failed to receive message from source", e);
}
return null;
}
public Message receive(long timeout) {
if (!this.initialized) {
throw new MessageHandlingException("adapter not initialized");
}
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
Future<Message> result = executor.submit(new Callable<Message>() {
public Message call() throws Exception {
return receive();
}
});
result.get(timeout, TimeUnit.MILLISECONDS);
if (result.isDone()) {
return result.get();
}
result.cancel(true);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
catch (TimeoutException e) {
return null;
}
catch (ExecutionException e) {
throw new MessageHandlingException("Exception occurred in message source", e);
}
finally {
executor.shutdown();
}
return null;
}
protected void initialize() {
}
protected abstract boolean sendObject(Object object) throws Exception;
protected abstract Object receiveObject() throws Exception;
}

View File

@@ -1,75 +0,0 @@
/*
* Copyright 2002-2007 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.endpoint;
import org.springframework.integration.message.MessageMapper;
import org.springframework.util.Assert;
/**
* An outbound channel adapter for invoking the specified method on the provided
* object. Delegates to a {@link MessageMapper} for converting between objects
* and messages. An optional {@link ArgumentListPreparer} may also be provided.
*
* @author Mark Fisher
*/
public class OutboundMethodInvokingChannelAdapter<T> extends AbstractOutboundChannelAdapter {
private T object;
private String method;
private SimpleMethodInvoker<T> invoker;
private ArgumentListPreparer argumentListPreparer;
public void setObject(T object) {
Assert.notNull(object, "'object' must not be null");
this.object = object;
}
public void setMethod(String method) {
Assert.notNull(method, "'method' must not be null");
this.method = method;
}
public void setArgumentListPreparer(ArgumentListPreparer argumentListPreparer) {
this.argumentListPreparer = argumentListPreparer;
}
@Override
public void initialize() {
this.invoker = new SimpleMethodInvoker<T>(this.object, this.method);
}
@Override
public boolean doSendObject(Object object) throws Exception {
Object args[] = null;
if (this.argumentListPreparer != null) {
args = this.argumentListPreparer.prepare(object);
}
else {
args = new Object[] { object };
}
Object result = this.invoker.invokeMethod(args);
if (result != null && logger.isWarnEnabled()) {
logger.warn("ignoring outbound channel adapter's return value");
}
return true;
}
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright 2002-2007 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.endpoint.file;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.endpoint.AbstractInboundChannelAdapter;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
/**
* A basic inbound channel adapter for polling a directory.
*
* @author Mark Fisher
*/
public class InboundFileAdapter extends AbstractInboundChannelAdapter {
private File directory;
private File copyToDirectory;
private FileFilter fileFilter;
private FilenameFilter filenameFilter;
private boolean isTextFile = true;
public InboundFileAdapter(File directory) {
Assert.notNull("directory must not be null");
this.directory = directory;
}
public void setCopyToDirectory(File copyToDirectory) {
this.copyToDirectory = copyToDirectory;
}
public void setFileFilter(FileFilter fileFilter) {
this.fileFilter = fileFilter;
}
public void setFilenameFilter(FilenameFilter filenameFilter) {
this.filenameFilter = filenameFilter;
}
public void setIsTextFile(boolean isTextFile) {
this.isTextFile = isTextFile;
}
@Override
protected Object doReceiveObject() {
File[] files = null;
if (this.fileFilter != null) {
files = this.directory.listFiles(fileFilter);
}
else if (this.filenameFilter != null) {
files = this.directory.listFiles(filenameFilter);
}
else {
files = this.directory.listFiles();
}
if (files == null) {
throw new MessageHandlingException("Problem occurred while polling for files. " +
"Is '" + directory.getAbsolutePath() + "' a directory?");
}
if (files.length == 0) {
return null;
}
File file = files[0];
try {
if (this.isTextFile) {
FileReader reader = new FileReader(file);
String result = FileCopyUtils.copyToString(reader);
if (this.copyToDirectory != null) {
FileWriter writer = new FileWriter(this.copyToDirectory.getAbsolutePath() + File.separator + file.getName());
FileCopyUtils.copy(new FileReader(file), writer);
}
file.delete();
return result;
}
return FileCopyUtils.copyToByteArray(file);
}
catch (Exception e) {
e.printStackTrace();
throw new MessageHandlingException("Failed to extract message", e);
}
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2002-2007 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.endpoint.jms;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import org.springframework.integration.MessagingConfigurationException;
import org.springframework.integration.endpoint.AbstractInboundChannelAdapter;
import org.springframework.jms.core.JmsTemplate;
/**
* A simple channel adapter for receiving JMS Messages with a polling listener.
*
* @author Mark Fisher
*/
public class JmsInboundAdapter extends AbstractInboundChannelAdapter {
private ConnectionFactory connectionFactory;
private Destination destination;
private JmsTemplate jmsTemplate;
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
@Override
public void initialize() {
if (this.jmsTemplate == null) {
if (this.connectionFactory == null || this.destination == null) {
throw new MessagingConfigurationException("Either a 'jmsTemplate' or " +
"*both* 'connectionFactory' and 'destination' are required.");
}
this.jmsTemplate = new JmsTemplate();
this.jmsTemplate.setConnectionFactory(this.connectionFactory);
this.jmsTemplate.setDefaultDestination(this.destination);
}
}
@Override
protected Object doReceiveObject() {
return this.jmsTemplate.receiveAndConvert();
}
}