Merge branch 'master' of git.springsource.org:spring-integration/spring-integration
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.springframework.integration;
|
||||
|
||||
/**
|
||||
* Exception that indicates an error during message delivery.
|
||||
* Exception that indicates an error occurred during message delivery.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.springframework.integration;
|
||||
|
||||
/**
|
||||
* Exception that indicates an error during message handling.
|
||||
* Exception that indicates an error occurred during message handling.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class MessageHeaders implements Map<String, Object>, Serializable {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MessageHeaders.class);
|
||||
|
||||
public static final String PREFIX = "$";
|
||||
public static final String PREFIX = "";
|
||||
|
||||
/**
|
||||
* The key for the Message ID. This is an automatically generated UUID and
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
package org.springframework.integration;
|
||||
|
||||
|
||||
/**
|
||||
* Exception that indicates a timeout elapsed prior to successful message delivery.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package org.springframework.integration;
|
||||
|
||||
|
||||
/**
|
||||
* The base exception for any failures within the messaging system.
|
||||
* The base exception for any failures related to messaging.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
@@ -26,7 +25,7 @@ package org.springframework.integration;
|
||||
@SuppressWarnings("serial")
|
||||
public class MessagingException extends RuntimeException {
|
||||
|
||||
private Message<?> failedMessage;
|
||||
private volatile Message<?> failedMessage;
|
||||
|
||||
|
||||
public MessagingException(Message<?> message) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,10 +18,10 @@ package org.springframework.integration.config.xml;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -33,13 +33,13 @@ public abstract class AbstractPollingInboundChannelAdapterParser extends Abstrac
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) {
|
||||
String source = this.parseSource(element, parserContext);
|
||||
if (!StringUtils.hasText(source)) {
|
||||
BeanMetadataElement source = this.parseSource(element, parserContext);
|
||||
if (source == null) {
|
||||
parserContext.getReaderContext().error("failed to parse source", element);
|
||||
}
|
||||
BeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SourcePollingChannelAdapterFactoryBean");
|
||||
adapterBuilder.addPropertyReference("source", source);
|
||||
adapterBuilder.addPropertyValue("source", source);
|
||||
adapterBuilder.addPropertyReference("outputChannel", channelName);
|
||||
Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
|
||||
if (pollerElement != null) {
|
||||
@@ -53,6 +53,6 @@ public abstract class AbstractPollingInboundChannelAdapterParser extends Abstrac
|
||||
* 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);
|
||||
protected abstract BeanMetadataElement parseSource(Element element, ParserContext parserContext);
|
||||
|
||||
}
|
||||
|
||||
@@ -191,8 +191,7 @@ public abstract class IntegrationNamespaceUtils {
|
||||
}
|
||||
|
||||
public static BeanComponentDefinition parseInnerHandlerDefinition(Element element, ParserContext parserContext) {
|
||||
// parses out inner bean definition for concrete implementation if
|
||||
// defined
|
||||
// parses out the inner bean definition for concrete implementation if defined
|
||||
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "bean");
|
||||
BeanComponentDefinition innerComponentDefinition = null;
|
||||
if (childElements != null && childElements.size() == 1) {
|
||||
@@ -202,15 +201,13 @@ public abstract class IntegrationNamespaceUtils {
|
||||
bdHolder = delegate.decorateBeanDefinitionIfRequired(beanElement, bdHolder);
|
||||
BeanDefinition inDef = bdHolder.getBeanDefinition();
|
||||
innerComponentDefinition = new BeanComponentDefinition(inDef, bdHolder.getBeanName());
|
||||
parserContext.registerBeanComponent(innerComponentDefinition);
|
||||
}
|
||||
|
||||
String ref = element.getAttribute(REF_ATTRIBUTE);
|
||||
Assert.isTrue(!(StringUtils.hasText(ref) && innerComponentDefinition != null),
|
||||
"Ambiguous definition. Inner bean "
|
||||
+ (innerComponentDefinition == null ? innerComponentDefinition : innerComponentDefinition
|
||||
.getBeanDefinition().getBeanClassName()) + " declaration and \"ref\" " + ref
|
||||
+ " are not allowed together.");
|
||||
"Ambiguous definition. Inner bean " + (innerComponentDefinition == null ? innerComponentDefinition
|
||||
: innerComponentDefinition.getBeanDefinition().getBeanClassName())
|
||||
+ " declaration and \"ref\" " + ref + " are not allowed together.");
|
||||
return innerComponentDefinition;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.util.List;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
@@ -38,39 +40,57 @@ import org.springframework.util.xml.DomUtils;
|
||||
public class MethodInvokingInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanMetadataElement result = null;
|
||||
BeanComponentDefinition innnerBeanDef = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
|
||||
String sourceRef = element.getAttribute("ref");
|
||||
String methodName = element.getAttribute("method");
|
||||
String expressionString = element.getAttribute("expression");
|
||||
if (innnerBeanDef != null) {
|
||||
if (StringUtils.hasText(sourceRef)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"inner bean and a 'ref' attribute are mutually exclusive options", element);
|
||||
}
|
||||
sourceRef = innnerBeanDef.getBeanName();
|
||||
if (StringUtils.hasText(methodName)) {
|
||||
result = this.parseMethodInvokingSource(innnerBeanDef, methodName, element, parserContext);
|
||||
}
|
||||
else {
|
||||
result = innnerBeanDef;
|
||||
}
|
||||
}
|
||||
else if (StringUtils.hasText(expressionString)) {
|
||||
if (StringUtils.hasText(sourceRef)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"the 'expression' and 'ref' attributes are mutually exclusive options", element);
|
||||
}
|
||||
sourceRef = this.parseExpression(expressionString, element, parserContext);
|
||||
String expressionBeanName = this.parseExpression(expressionString, element, parserContext);
|
||||
result = new RuntimeBeanReference(expressionBeanName);
|
||||
}
|
||||
if (!StringUtils.hasText(sourceRef)) {
|
||||
else if (StringUtils.hasText(sourceRef)) {
|
||||
BeanMetadataElement sourceValue = new RuntimeBeanReference(sourceRef);
|
||||
if (StringUtils.hasText(methodName)) {
|
||||
result = this.parseMethodInvokingSource(sourceValue, methodName, element, parserContext);
|
||||
}
|
||||
else {
|
||||
result = sourceValue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error("One of the following is required: " +
|
||||
"'ref' attribute, 'expression' attribute, or an inner-bean definition.", element);
|
||||
}
|
||||
String methodName = element.getAttribute("method");
|
||||
if (StringUtils.hasText(methodName)) {
|
||||
BeanDefinitionBuilder sourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".endpoint.MethodInvokingMessageSource");
|
||||
sourceBuilder.addPropertyReference("object", sourceRef);
|
||||
sourceBuilder.addPropertyValue("methodName", methodName);
|
||||
this.parseHeaderExpressions(sourceBuilder, element, parserContext);
|
||||
sourceRef = BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
sourceBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
}
|
||||
return sourceRef;
|
||||
return result;
|
||||
}
|
||||
|
||||
private BeanMetadataElement parseMethodInvokingSource(BeanMetadataElement targetObject, String methodName, Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder sourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".endpoint.MethodInvokingMessageSource");
|
||||
sourceBuilder.addPropertyValue("object", targetObject);
|
||||
sourceBuilder.addPropertyValue("methodName", methodName);
|
||||
this.parseHeaderExpressions(sourceBuilder, element, parserContext);
|
||||
String sourceRef = BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
sourceBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return new RuntimeBeanReference(sourceRef);
|
||||
}
|
||||
|
||||
private String parseExpression(String expressionString, Element element, ParserContext parserContext) {
|
||||
|
||||
@@ -13,9 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.context;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
@@ -26,14 +29,25 @@ import org.springframework.context.support.ConversionServiceFactoryBean;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @sini 2.0
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
class ConversionServiceCreator implements BeanFactoryPostProcessor {
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
if (!beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME)){
|
||||
BeanDefinitionBuilder conversionServiceBuilder = BeanDefinitionBuilder.rootBeanDefinition(ConversionServiceFactoryBean.class);
|
||||
BeanDefinitionHolder csHolder = new BeanDefinitionHolder(conversionServiceBuilder.getBeanDefinition(), IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(csHolder, (BeanDefinitionRegistry) beanFactory);
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
if (!beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME)) {
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
BeanDefinitionBuilder conversionServiceBuilder = BeanDefinitionBuilder.rootBeanDefinition(ConversionServiceFactoryBean.class);
|
||||
BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(
|
||||
conversionServiceBuilder.getBeanDefinition(), IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, (BeanDefinitionRegistry) beanFactory);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn("BeanFactory is not a BeanDefinitionRegistry implementation. Cannot register a default ConversionService.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,39 +13,53 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.context;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility class that keeps track of a set of Converters in order to register
|
||||
* them with the "integrationConversionService" upon initialization.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
class ConverterRegistrar implements InitializingBean, BeanFactoryAware {
|
||||
|
||||
private final Set<Converter<?, ?>> converters;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public ConverterRegistrar(Set<Converter<?, ?>> converters){
|
||||
|
||||
|
||||
public ConverterRegistrar(Set<Converter<?, ?>> converters) {
|
||||
this.converters = converters;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
GenericConversionService conversionService = beanFactory.getBean(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, GenericConversionService.class);
|
||||
Assert.notNull(conversionService, "can not locate '" + IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME + "' ");
|
||||
ConversionServiceFactory.registerConverters(converters, conversionService);
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(beanFactory, "BeanFactory is required");
|
||||
ConversionService conversionService = IntegrationContextUtils.getConversionService(beanFactory);
|
||||
if (conversionService instanceof GenericConversionService) {
|
||||
ConversionServiceFactory.registerConverters(converters, (GenericConversionService) conversionService);
|
||||
}
|
||||
else {
|
||||
Assert.notNull(conversionService, "Failed to locate '" + IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.context.metadata.MetadataStore;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.integration.store.MetadataStore;
|
||||
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
|
||||
|
||||
@@ -13,21 +13,33 @@
|
||||
|
||||
package org.springframework.integration.control;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.handler.AbstractMessageProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A MessageProcessor implementation that expects an Expression or expressionString
|
||||
* as the Message payload. When processing, it simply evaluates that expression.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
public class ExpressionPayloadMessageProcessor extends AbstractMessageProcessor<Object> {
|
||||
|
||||
|
||||
/**
|
||||
* Evaluates the Message payload expression.
|
||||
* @throws IllegalArgumentException if the payload is not an Exception or String
|
||||
*/
|
||||
public Object processMessage(Message<?> message) {
|
||||
Assert.state(message.getPayload() instanceof String, "Message payload must be a String expression");
|
||||
String expression = (String) message.getPayload();
|
||||
return evaluateExpression(expression, message);
|
||||
Object expression = message.getPayload();
|
||||
if (expression instanceof Expression) {
|
||||
return evaluateExpression((Expression) expression, message);
|
||||
}
|
||||
if (expression instanceof String) {
|
||||
return evaluateExpression((String) expression, message);
|
||||
}
|
||||
throw new IllegalArgumentException("Message payload must be an Expression instance or an expression String.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
@@ -39,7 +40,6 @@ import org.springframework.integration.annotation.Headers;
|
||||
import org.springframework.integration.annotation.Payload;
|
||||
import org.springframework.integration.mapping.InboundMessageMapper;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.util.SimpleBeanResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -106,7 +106,7 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
|
||||
|
||||
public void setBeanFactory(final BeanFactory beanFactory) {
|
||||
if (beanFactory != null) {
|
||||
this.beanResolver = new SimpleBeanResolver(beanFactory);
|
||||
this.beanResolver = new BeanFactoryResolver(beanFactory);
|
||||
this.evaluationContext.setBeanResolver(beanResolver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.context.metadata;
|
||||
package org.springframework.integration.store;
|
||||
|
||||
/**
|
||||
* Strategy interface for storing metadata from certain adapters
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.context.metadata;
|
||||
package org.springframework.integration.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -11,7 +11,7 @@
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.context.metadata;
|
||||
package org.springframework.integration.store;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -15,6 +15,7 @@ package org.springframework.integration.util;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
@@ -50,7 +51,7 @@ public abstract class AbstractExpressionEvaluator implements BeanFactoryAware {
|
||||
public void setBeanFactory(final BeanFactory beanFactory) {
|
||||
if (beanFactory != null) {
|
||||
this.typeConverter.setBeanFactory(beanFactory);
|
||||
this.evaluationContext.setBeanResolver(new SimpleBeanResolver(beanFactory));
|
||||
this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SimpleBeanResolver implements BeanResolver {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
|
||||
public SimpleBeanResolver(BeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "beanFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
public final Object resolve(EvaluationContext context, String beanName) throws AccessException {
|
||||
return this.beanFactory.getBean(beanName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2622,10 +2622,9 @@ The list of component name patterns you want to track (e.g., tracked-components
|
||||
<xsd:element name="control-bus">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Control bus that accepts messages in the form of Groovy scripts. The scripts should be provided as
|
||||
String payloads
|
||||
in incoming messages. Scripts can refer to beans in the context using the standard @beanName
|
||||
convention.
|
||||
Control bus that accepts messages in the form of SpEL expressions. The expressions should be provided
|
||||
either as Expression or String payloads (that can be parsed into valid Expressions). in incoming messages.
|
||||
The expressions can refer to beans in the context using the standard @beanName convention.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<!-- see INT-1528 -->
|
||||
<service-activator id="testEndpoint" input-channel="input">
|
||||
<beans:bean class="org.springframework.integration.config.xml.InnerBeanConfigTests$TestBean"/>
|
||||
</service-activator>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class InnerBeanConfigTests {
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer testEndpoint;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
// INT-1528: the inner bean should not be registered in the context
|
||||
@Test(expected = NoSuchBeanDefinitionException.class)
|
||||
public void checkInnerBean() {
|
||||
Object innerBean = TestUtils.getPropertyValue(testEndpoint, "handler.processor.delegate.targetObject");
|
||||
assertNotNull(innerBean);
|
||||
context.getBean(TestBean.class);
|
||||
}
|
||||
|
||||
|
||||
public static class TestBean {
|
||||
public String echo(String value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,6 @@
|
||||
default-request-channel="requestChannel"
|
||||
service-interface="org.springframework.integration.gateway.GatewayWithHeaderAnnotations$TestService" />
|
||||
|
||||
<service-activator input-channel="requestChannel" expression="payload + headers.$priority"/>
|
||||
<service-activator input-channel="requestChannel" expression="payload + headers.priority + headers.$custom"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -43,13 +43,14 @@ public class GatewayWithHeaderAnnotations {
|
||||
@Test // INT-1205
|
||||
public void priorityAsArgument() {
|
||||
TestService gateway = (TestService) applicationContext.getBean("gateway");
|
||||
String result = gateway.test("foo", 99);
|
||||
assertEquals("foo99", result);
|
||||
String result = gateway.test("foo", 99, "bar");
|
||||
assertEquals("foo99bar", result);
|
||||
}
|
||||
|
||||
|
||||
public static interface TestService {
|
||||
public String test(String str, @Header(MessageHeaders.PRIORITY) int priority);
|
||||
// wrt INT-1205, priority no longer has a $ prefix, so here we are testing the $custom header as well
|
||||
public String test(String str, @Header(MessageHeaders.PRIORITY) int priority, @Header("$custom") String custom);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
|
||||
/**
|
||||
@@ -112,18 +114,18 @@ public class ExpressionEvaluatingMessageProcessorTests {
|
||||
|
||||
@Test
|
||||
public void testProcessMessageWithDollarInBrackets() {
|
||||
Expression expression = expressionParser.parseExpression("headers['$id']");
|
||||
Expression expression = expressionParser.parseExpression("headers['$foo_id']");
|
||||
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
|
||||
GenericMessage<String> message = new GenericMessage<String>("foo");
|
||||
assertEquals(message.getHeaders().getId(), processor.processMessage(message));
|
||||
Message<String> message = MessageBuilder.withPayload("foo").setHeader("$foo_id", "abc").build();
|
||||
assertEquals("abc", processor.processMessage(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessMessageWithDollarPropertyAccess() {
|
||||
Expression expression = expressionParser.parseExpression("headers.$id");
|
||||
Expression expression = expressionParser.parseExpression("headers.$foo_id");
|
||||
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
|
||||
GenericMessage<String> message = new GenericMessage<String>("foo");
|
||||
assertEquals(message.getHeaders().getId(), processor.processMessage(message));
|
||||
Message<String> message = MessageBuilder.withPayload("foo").setHeader("$foo_id", "xyz").build();
|
||||
assertEquals("xyz", processor.processMessage(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -59,7 +59,7 @@ public class JsonInboundMessageMapperTests {
|
||||
@Test
|
||||
public void testToMessageWithHeadersAndStringPayload() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":\"myPayloadStuff\"}";
|
||||
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\"},\"payload\":\"myPayloadStuff\"}";
|
||||
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff").setHeader(MessageHeaders.TIMESTAMP, new Long(1)).setHeader(MessageHeaders.ID, id).build();
|
||||
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(String.class);
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
@@ -80,7 +80,7 @@ public class JsonInboundMessageMapperTests {
|
||||
public void testToMessageWithHeadersAndBeanPayload() throws Exception {
|
||||
TestBean bean = new TestBean();
|
||||
UUID id = UUID.randomUUID();
|
||||
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":" + getBeanAsJson(bean) + "}";
|
||||
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\"},\"payload\":" + getBeanAsJson(bean) + "}";
|
||||
Message<TestBean> expected = MessageBuilder.withPayload(bean).setHeader(MessageHeaders.TIMESTAMP, new Long(1)).setHeader(MessageHeaders.ID, id).build();
|
||||
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(TestBean.class);
|
||||
Message<?> result = mapper.toMessage(jsonMessage);
|
||||
@@ -101,7 +101,7 @@ public class JsonInboundMessageMapperTests {
|
||||
public void testToMessageWithBeanHeaderAndStringPayload() throws Exception {
|
||||
TestBean bean = new TestBean();
|
||||
UUID id = UUID.randomUUID();
|
||||
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\", \"myHeader\":" + getBeanAsJson(bean) + "},\"payload\":\"myPayloadStuff\"}";
|
||||
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\", \"myHeader\":" + getBeanAsJson(bean) + "},\"payload\":\"myPayloadStuff\"}";
|
||||
Message<String> expected = MessageBuilder.withPayload("myPayloadStuff").
|
||||
setHeader(MessageHeaders.TIMESTAMP, new Long(1)).setHeader(MessageHeaders.ID, id).setHeader("myHeader", bean).build();
|
||||
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(String.class);
|
||||
@@ -115,7 +115,7 @@ public class JsonInboundMessageMapperTests {
|
||||
@Test
|
||||
public void testToMessageWithHeadersAndListOfStringsPayload() throws Exception {
|
||||
UUID id = UUID.randomUUID();
|
||||
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":[\"myPayloadStuff1\",\"myPayloadStuff2\",\"myPayloadStuff3\"]}";
|
||||
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\"},\"payload\":[\"myPayloadStuff1\",\"myPayloadStuff2\",\"myPayloadStuff3\"]}";
|
||||
List<String> expectedList = Arrays.asList(new String[]{"myPayloadStuff1", "myPayloadStuff2", "myPayloadStuff3"});
|
||||
Message<List<String>> expected = MessageBuilder.withPayload(expectedList).setHeader(MessageHeaders.TIMESTAMP, new Long(1)).setHeader(MessageHeaders.ID, id).build();
|
||||
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(new TypeReference<List<String>>(){});
|
||||
@@ -128,7 +128,7 @@ public class JsonInboundMessageMapperTests {
|
||||
TestBean bean1 = new TestBean();
|
||||
TestBean bean2 = new TestBean();
|
||||
UUID id = UUID.randomUUID();
|
||||
String jsonMessage = "{\"headers\":{\"$timestamp\":1,\"$id\":\"" + id + "\"},\"payload\":[" + getBeanAsJson(bean1) + "," + getBeanAsJson(bean2) + "]}";
|
||||
String jsonMessage = "{\"headers\":{\"timestamp\":1,\"id\":\"" + id + "\"},\"payload\":[" + getBeanAsJson(bean1) + "," + getBeanAsJson(bean2) + "]}";
|
||||
List<TestBean> expectedList = Arrays.asList(new TestBean[]{bean1, bean2});
|
||||
Message<List<TestBean>> expected = MessageBuilder.withPayload(expectedList).setHeader(MessageHeaders.TIMESTAMP, new Long(1)).setHeader(MessageHeaders.ID, id).build();
|
||||
JsonInboundMessageMapper mapper = new JsonInboundMessageMapper(new TypeReference<List<TestBean>>(){});
|
||||
|
||||
@@ -50,8 +50,8 @@ public class JsonOutboundMessageMapperTests {
|
||||
JsonOutboundMessageMapper mapper = new JsonOutboundMessageMapper();
|
||||
String result = mapper.fromMessage(testMessage);
|
||||
assertTrue(result.contains("\"headers\":{"));
|
||||
assertTrue(result.contains("\"$timestamp\":"+testMessage.getHeaders().getTimestamp()));
|
||||
assertTrue(result.contains("\"$id\":\""+testMessage.getHeaders().getId()+"\""));
|
||||
assertTrue(result.contains("\"timestamp\":"+testMessage.getHeaders().getTimestamp()));
|
||||
assertTrue(result.contains("\"id\":\""+testMessage.getHeaders().getId()+"\""));
|
||||
assertTrue(result.contains("\"payload\":\"myPayloadStuff\""));
|
||||
}
|
||||
|
||||
@@ -64,10 +64,10 @@ public class JsonOutboundMessageMapperTests {
|
||||
JsonOutboundMessageMapper mapper = new JsonOutboundMessageMapper();
|
||||
String result = mapper.fromMessage(testMessage);
|
||||
assertTrue(result.contains("\"headers\":{"));
|
||||
assertTrue(result.contains("\"$timestamp\":"+testMessage.getHeaders().getTimestamp()));
|
||||
assertTrue(result.contains("\"$id\":\""+testMessage.getHeaders().getId()+"\""));
|
||||
assertTrue(result.contains("\"timestamp\":"+testMessage.getHeaders().getTimestamp()));
|
||||
assertTrue(result.contains("\"id\":\""+testMessage.getHeaders().getId()+"\""));
|
||||
assertTrue(result.contains("\"payload\":\"myPayloadStuff\""));
|
||||
assertTrue(result.contains("\"$history\":"));
|
||||
assertTrue(result.contains("\"history\":"));
|
||||
assertTrue(result.contains("testName-1"));
|
||||
assertTrue(result.contains("testType-1"));
|
||||
assertTrue(result.contains("testName-2"));
|
||||
@@ -93,8 +93,8 @@ public class JsonOutboundMessageMapperTests {
|
||||
JsonOutboundMessageMapper mapper = new JsonOutboundMessageMapper();
|
||||
String result = mapper.fromMessage(testMessage);
|
||||
assertTrue(result.contains("\"headers\":{"));
|
||||
assertTrue(result.contains("\"$timestamp\":"+testMessage.getHeaders().getTimestamp()));
|
||||
assertTrue(result.contains("\"$id\":\""+testMessage.getHeaders().getId()+"\""));
|
||||
assertTrue(result.contains("\"timestamp\":"+testMessage.getHeaders().getTimestamp()));
|
||||
assertTrue(result.contains("\"id\":\""+testMessage.getHeaders().getId()+"\""));
|
||||
TestBean parsedPayload = extractJsonPayloadToTestBean(result);
|
||||
assertEquals(payload, parsedPayload);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.context.metadata;
|
||||
package org.springframework.integration.store;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
@@ -27,6 +27,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.integration.store.PropertiesPersistingMetadataStore;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -53,10 +54,10 @@ public class PropertiesPersistingMetadataStoreTests {
|
||||
|
||||
@Test
|
||||
public void validateWithCustomBaseDir() throws Exception {
|
||||
File file = new File("foo" + "/metadata-store.properties");
|
||||
File file = new File("target/foo" + "/metadata-store.properties");
|
||||
file.deleteOnExit();
|
||||
PropertiesPersistingMetadataStore metadataStore = new PropertiesPersistingMetadataStore();
|
||||
metadataStore.setBaseDirectory("foo");
|
||||
metadataStore.setBaseDirectory("target/foo");
|
||||
metadataStore.afterPropertiesSet();
|
||||
metadataStore.put("foo", "bar");
|
||||
metadataStore.destroy();
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.feed;
|
||||
|
||||
/**
|
||||
* Provides a place to store the header keys for {@link FeedReaderMessageSource}
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FeedConstants {
|
||||
static public final String FEED_URL = "FEED_URL";
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* 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.feed;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.store.MetadataStore;
|
||||
import org.springframework.integration.store.SimpleMetadataStore;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
import com.sun.syndication.feed.synd.SyndFeed;
|
||||
import com.sun.syndication.fetcher.FeedFetcher;
|
||||
import com.sun.syndication.fetcher.FetcherEvent;
|
||||
import com.sun.syndication.fetcher.FetcherListener;
|
||||
import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache;
|
||||
import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
|
||||
|
||||
/**
|
||||
* This implementation of {@link MessageSource} will produce individual
|
||||
* {@link SyndEntry}s for a feed identified with the 'feedUrl' attribute.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Mario Gray
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FeedEntryMessageSource extends IntegrationObjectSupport implements MessageSource<SyndEntry> {
|
||||
|
||||
private final URL feedUrl;
|
||||
|
||||
private final FeedFetcher feedFetcher;
|
||||
|
||||
private final Queue<SyndFeed> feeds = new ConcurrentLinkedQueue<SyndFeed>();
|
||||
|
||||
private final Queue<SyndEntry> entries = new ConcurrentLinkedQueue<SyndEntry>();
|
||||
|
||||
private volatile String metadataKey;
|
||||
|
||||
private volatile MetadataStore metadataStore;
|
||||
|
||||
private volatile long lastTime = -1;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final Comparator<SyndEntry> syndEntryComparator = new SyndEntryComparator();
|
||||
|
||||
private final Object feedMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a FeedEntryMessageSource that will use a HttpURLFeedFetcher to read feeds from the given URL.
|
||||
* If the feed URL has a protocol other than http*, consider providing a custom implementation of the
|
||||
* {@link FeedFetcher} via the alternate constructor.
|
||||
*/
|
||||
public FeedEntryMessageSource(URL feedUrl) {
|
||||
this(feedUrl, new HttpURLFeedFetcher(HashMapFeedInfoCache.getInstance()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FeedEntryMessageSource that will use the provided FeedFetcher to read from the given feed URL.
|
||||
*/
|
||||
public FeedEntryMessageSource(URL feedUrl, FeedFetcher feedFetcher) {
|
||||
Assert.notNull(feedUrl, "feedUrl must not be null");
|
||||
Assert.notNull(feedFetcher, "feedFetcher must not be null");
|
||||
this.feedUrl = feedUrl;
|
||||
this.feedFetcher = feedFetcher;
|
||||
}
|
||||
|
||||
|
||||
public void setMetadataStore(MetadataStore metadataStore) {
|
||||
Assert.notNull(metadataStore, "metadataStore must not be null");
|
||||
this.metadataStore = metadataStore;
|
||||
}
|
||||
|
||||
public String getComponentType() {
|
||||
return "feed:inbound-channel-adapter";
|
||||
}
|
||||
|
||||
public Message<SyndEntry> receive() {
|
||||
Assert.isTrue(this.initialized, "'FeedEntryReaderMessageSource' must be initialized before it can produce Messages.");
|
||||
SyndEntry entry = doReceive();
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
return MessageBuilder.withPayload(entry).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
this.feedFetcher.addFetcherEventListener(new FeedQueueUpdatingFetcherListener());
|
||||
if (this.metadataStore == null) {
|
||||
// first try to look for a 'messageStore' in the context
|
||||
BeanFactory beanFactory = this.getBeanFactory();
|
||||
if (beanFactory != null) {
|
||||
this.metadataStore = IntegrationContextUtils.getMetadataStore(beanFactory);
|
||||
}
|
||||
// if no 'messageStore' in context, fall back to in-memory Map-based default
|
||||
if (this.metadataStore == null) {
|
||||
this.metadataStore = new SimpleMetadataStore();
|
||||
}
|
||||
}
|
||||
StringBuilder metadataKeyBuilder = new StringBuilder();
|
||||
if (StringUtils.hasText(this.getComponentType())) {
|
||||
metadataKeyBuilder.append(this.getComponentType() + ".");
|
||||
}
|
||||
if (StringUtils.hasText(this.getComponentName())) {
|
||||
metadataKeyBuilder.append(this.getComponentName() + ".");
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn("FeedEntryMessageSource has no name. MetadataStore key might not be unique.");
|
||||
}
|
||||
metadataKeyBuilder.append(this.feedUrl);
|
||||
this.metadataKey = metadataKeyBuilder.toString();
|
||||
String lastTimeValue = this.metadataStore.get(this.metadataKey);
|
||||
if (StringUtils.hasText(lastTimeValue)) {
|
||||
this.lastTime = Long.parseLong(lastTimeValue);
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
private SyndEntry doReceive() {
|
||||
SyndEntry nextEntry = null;
|
||||
synchronized (this.monitor) {
|
||||
nextEntry = getNextEntry();
|
||||
if (nextEntry == null) {
|
||||
// read feed and try again
|
||||
this.populateEntryList();
|
||||
nextEntry = getNextEntry();
|
||||
}
|
||||
}
|
||||
return nextEntry;
|
||||
}
|
||||
|
||||
private SyndEntry getNextEntry() {
|
||||
SyndEntry next = this.entries.poll();
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
this.lastTime = next.getPublishedDate().getTime();
|
||||
this.metadataStore.put(this.metadataKey, this.lastTime + "");
|
||||
return next;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void populateEntryList() {
|
||||
SyndFeed syndFeed = this.getFeed();
|
||||
if (syndFeed != null) {
|
||||
List<SyndEntry> retrievedEntries = (List<SyndEntry>) syndFeed.getEntries();
|
||||
if (!CollectionUtils.isEmpty(retrievedEntries)) {
|
||||
Collections.sort(retrievedEntries, this.syndEntryComparator);
|
||||
for (SyndEntry entry : retrievedEntries) {
|
||||
if (entry.getPublishedDate().getTime() > this.lastTime) {
|
||||
this.entries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SyndFeed getFeed() {
|
||||
SyndFeed feed = null;
|
||||
try {
|
||||
synchronized (this.feedMonitor) {
|
||||
feed = this.feedFetcher.retrieveFeed(this.feedUrl);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("retrieved feed at url '" + this.feedUrl + "'");
|
||||
}
|
||||
if (feed == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("no feeds updated, returning null");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException(
|
||||
"Failed to retrieve feed at url '" + this.feedUrl + "'", e);
|
||||
}
|
||||
return feed;
|
||||
}
|
||||
|
||||
|
||||
private static class SyndEntryComparator implements Comparator<SyndEntry> {
|
||||
|
||||
public int compare(SyndEntry entry1, SyndEntry entry2) {
|
||||
return entry1.getPublishedDate().compareTo(entry2.getPublishedDate());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class FeedQueueUpdatingFetcherListener implements FetcherListener {
|
||||
|
||||
/**
|
||||
* @see com.sun.syndication.fetcher.FetcherListener#fetcherEvent(com.sun.syndication.fetcher.FetcherEvent)
|
||||
*/
|
||||
public void fetcherEvent(final FetcherEvent event) {
|
||||
String eventType = event.getEventType();
|
||||
if (FetcherEvent.EVENT_TYPE_FEED_POLLED.equals(eventType)) {
|
||||
logger.debug("\tEVENT: Feed Polled. URL = " + event.getUrlString());
|
||||
}
|
||||
else if (FetcherEvent.EVENT_TYPE_FEED_RETRIEVED.equals(eventType)) {
|
||||
logger.debug("\tEVENT: Feed Retrieved. URL = " + event.getUrlString());
|
||||
feeds.add(event.getFeed());
|
||||
}
|
||||
else if (FetcherEvent.EVENT_TYPE_FEED_UNCHANGED.equals(eventType)) {
|
||||
logger.debug("\tEVENT: Feed Unchanged. URL = " + event.getUrlString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* 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.feed;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.context.metadata.MetadataStore;
|
||||
import org.springframework.integration.context.metadata.SimpleMetadataStore;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
import com.sun.syndication.feed.synd.SyndFeed;
|
||||
|
||||
/**
|
||||
* This implementation of {@link MessageSource} will produce individual
|
||||
* {@link SyndEntry}s for a feed identified with the 'feedUrl' attribute.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Mario Gray
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FeedEntryReaderMessageSource extends IntegrationObjectSupport implements MessageSource<SyndEntry> {
|
||||
|
||||
private final Queue<SyndEntry> entries = new ConcurrentLinkedQueue<SyndEntry>();
|
||||
|
||||
private final FeedReaderMessageSource feedReaderMessageSource;
|
||||
|
||||
private volatile String metadataKey;
|
||||
|
||||
private volatile MetadataStore metadataStore;
|
||||
|
||||
private volatile long lastTime = -1;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final Comparator<SyndEntry> syndEntryComparator = new SyndEntryComparator();
|
||||
|
||||
|
||||
public FeedEntryReaderMessageSource(FeedReaderMessageSource feedReaderMessageSource) {
|
||||
Assert.notNull(feedReaderMessageSource, "'feedReaderMessageSource' must not be null");
|
||||
this.feedReaderMessageSource = feedReaderMessageSource;
|
||||
}
|
||||
|
||||
|
||||
public void setMetadataStore(MetadataStore metadataStore) {
|
||||
Assert.notNull(metadataStore, "metadataStore must not be null");
|
||||
this.metadataStore = metadataStore;
|
||||
}
|
||||
|
||||
public String getComponentType() {
|
||||
return "feed:inbound-channel-adapter";
|
||||
}
|
||||
|
||||
public Message<SyndEntry> receive() {
|
||||
Assert.isTrue(this.initialized, "'FeedEntryReaderMessageSource' must be initialized before it can produce Messages.");
|
||||
SyndEntry entry = doReceive();
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
return MessageBuilder.withPayload(entry).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
if (this.metadataStore == null) {
|
||||
// first try to look for a 'messageStore' in the context
|
||||
BeanFactory beanFactory = this.getBeanFactory();
|
||||
if (beanFactory != null) {
|
||||
MetadataStore metadataStore = IntegrationContextUtils.getMetadataStore(beanFactory);
|
||||
if (metadataStore != null) {
|
||||
this.metadataStore = metadataStore;
|
||||
}
|
||||
}
|
||||
if (this.metadataStore == null) {
|
||||
this.metadataStore = new SimpleMetadataStore();
|
||||
}
|
||||
}
|
||||
Assert.hasText(this.getComponentName(), "FeedEntryReaderMessageSource must have a name");
|
||||
this.metadataKey = this.getComponentType() + "." + this.getComponentName()
|
||||
+ "." + this.feedReaderMessageSource.getFeedUrl();
|
||||
String lastTimeValue = this.metadataStore.get(this.metadataKey);
|
||||
if (StringUtils.hasText(lastTimeValue)) {
|
||||
this.lastTime = Long.parseLong(lastTimeValue);
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private SyndEntry doReceive() {
|
||||
SyndEntry nextUp = null;
|
||||
synchronized (this.monitor) {
|
||||
nextUp = pollAndCache();
|
||||
if (nextUp != null) {
|
||||
return nextUp;
|
||||
}
|
||||
// otherwise, fill the backlog
|
||||
SyndFeed syndFeed = this.feedReaderMessageSource.receiveSyndFeed();
|
||||
if (syndFeed != null) {
|
||||
List<SyndEntry> feedEntries = (List<SyndEntry>) syndFeed.getEntries();
|
||||
if (null != feedEntries) {
|
||||
Collections.sort(feedEntries, syndEntryComparator);
|
||||
for (SyndEntry se : feedEntries) {
|
||||
long publishedTime = se.getPublishedDate().getTime();
|
||||
if (publishedTime > this.lastTime) {
|
||||
entries.add(se);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nextUp = pollAndCache();
|
||||
}
|
||||
return nextUp;
|
||||
}
|
||||
|
||||
private SyndEntry pollAndCache() {
|
||||
SyndEntry next = this.entries.poll();
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
this.lastTime = next.getPublishedDate().getTime();
|
||||
this.metadataStore.put(this.metadataKey, this.lastTime + "");
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
private static class SyndEntryComparator implements Comparator<SyndEntry> {
|
||||
|
||||
public int compare(SyndEntry entry1, SyndEntry entry2) {
|
||||
if (entry1 == null || entry2 == null) {
|
||||
|
||||
}
|
||||
return entry1.getPublishedDate().compareTo(entry2.getPublishedDate());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* 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.feed;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.sun.syndication.feed.synd.SyndFeed;
|
||||
import com.sun.syndication.fetcher.FetcherEvent;
|
||||
import com.sun.syndication.fetcher.FetcherListener;
|
||||
import com.sun.syndication.fetcher.impl.AbstractFeedFetcher;
|
||||
import com.sun.syndication.fetcher.impl.FeedFetcherCache;
|
||||
import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache;
|
||||
import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
|
||||
|
||||
|
||||
/**
|
||||
* This implementation of {@link MessageSource} will produce {@link SyndFeed} for a feed identified
|
||||
* with 'feedUrl' attribute.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Mario Gray
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
public class FeedReaderMessageSource extends IntegrationObjectSupport
|
||||
implements InitializingBean, MessageSource<SyndFeed> {
|
||||
|
||||
private final AbstractFeedFetcher fetcher;
|
||||
private final Object syndFeedMonitor = new Object();
|
||||
|
||||
private volatile URL feedUrl;
|
||||
private volatile FeedFetcherCache fetcherCache;
|
||||
private volatile ConcurrentLinkedQueue<SyndFeed> syndFeeds = new ConcurrentLinkedQueue<SyndFeed>();
|
||||
private volatile MyFetcherListener myFetcherListener;
|
||||
|
||||
|
||||
public FeedReaderMessageSource(URL feedUrl) {
|
||||
this.feedUrl = feedUrl;
|
||||
if (feedUrl.getProtocol().equals("file")){
|
||||
fetcher = new FileUrlFeedFetcher();
|
||||
}
|
||||
else if (feedUrl.getProtocol().equals("http")){
|
||||
fetcherCache = HashMapFeedInfoCache.getInstance();
|
||||
fetcher = new HttpURLFeedFetcher(fetcherCache);
|
||||
}
|
||||
else{
|
||||
throw new IllegalArgumentException("Unsupported URL protocol: " + feedUrl.getProtocol());
|
||||
}
|
||||
}
|
||||
|
||||
public URL getFeedUrl() {
|
||||
return feedUrl;
|
||||
}
|
||||
|
||||
public SyndFeed receiveSyndFeed() {
|
||||
SyndFeed returnedSyndFeed = null;
|
||||
|
||||
try {
|
||||
synchronized (syndFeedMonitor) {
|
||||
returnedSyndFeed = fetcher.retrieveFeed(this.feedUrl);
|
||||
logger.debug("attempted to retrieve feed '" + this.feedUrl + "'");
|
||||
|
||||
if (returnedSyndFeed == null) {
|
||||
logger.debug("no feeds updated, return null!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Exception thrown when trying to retrive feed at url '" + this.feedUrl + "'", e);
|
||||
}
|
||||
|
||||
return returnedSyndFeed;
|
||||
}
|
||||
|
||||
public Message<SyndFeed> receive() {
|
||||
SyndFeed syndFeed = this.receiveSyndFeed();
|
||||
|
||||
if (null == syndFeed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MessageBuilder.withPayload(syndFeed).setHeader(FeedConstants.FEED_URL, this.feedUrl).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
|
||||
|
||||
|
||||
fetcher.addFetcherEventListener(myFetcherListener);
|
||||
Assert.notNull(this.feedUrl, "the feedURL can't be null");
|
||||
}
|
||||
|
||||
class MyFetcherListener implements FetcherListener {
|
||||
/**
|
||||
* @see com.sun.syndication.fetcher.FetcherListener#fetcherEvent(com.sun.syndication.fetcher.FetcherEvent)
|
||||
*/
|
||||
public void fetcherEvent(final FetcherEvent event) {
|
||||
String eventType = event.getEventType();
|
||||
|
||||
if (FetcherEvent.EVENT_TYPE_FEED_POLLED.equals(eventType)) {
|
||||
logger.debug("\tEVENT: Feed Polled. URL = " + event.getUrlString());
|
||||
} else if (FetcherEvent.EVENT_TYPE_FEED_RETRIEVED.equals(eventType)) {
|
||||
logger.debug("\tEVENT: Feed Retrieved. URL = " + event.getUrlString());
|
||||
syndFeeds.add(event.getFeed());
|
||||
} else if (FetcherEvent.EVENT_TYPE_FEED_UNCHANGED.equals(eventType)) {
|
||||
logger.debug("\tEVENT: Feed Unchanged. URL = " + event.getUrlString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.feed;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.sun.syndication.feed.synd.SyndFeed;
|
||||
import com.sun.syndication.fetcher.FetcherEvent;
|
||||
import com.sun.syndication.fetcher.FetcherException;
|
||||
import com.sun.syndication.fetcher.impl.AbstractFeedFetcher;
|
||||
import com.sun.syndication.fetcher.impl.SyndFeedInfo;
|
||||
import com.sun.syndication.io.FeedException;
|
||||
import com.sun.syndication.io.SyndFeedInput;
|
||||
import com.sun.syndication.io.XmlReader;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FileUrlFeedFetcher extends AbstractFeedFetcher {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.sun.syndication.fetcher.FeedFetcher#retrieveFeed(java.net.URL)
|
||||
*/
|
||||
public SyndFeed retrieveFeed(URL feedUrl) throws IllegalArgumentException,
|
||||
IOException, FeedException, FetcherException {
|
||||
if (feedUrl == null) {
|
||||
throw new IllegalArgumentException("null is not a valid URL");
|
||||
}
|
||||
|
||||
URLConnection connection = feedUrl.openConnection();
|
||||
|
||||
SyndFeedInfo syndFeedInfo = new SyndFeedInfo();
|
||||
retrieveAndCacheFeed(feedUrl, syndFeedInfo, connection);
|
||||
return syndFeedInfo.getSyndFeed();
|
||||
}
|
||||
|
||||
protected void retrieveAndCacheFeed(URL feedUrl, SyndFeedInfo syndFeedInfo, URLConnection connection) throws IllegalArgumentException, FeedException, FetcherException, IOException {
|
||||
resetFeedInfo(feedUrl, syndFeedInfo, connection);
|
||||
}
|
||||
|
||||
protected void resetFeedInfo(URL orignalUrl, SyndFeedInfo syndFeedInfo, URLConnection connection) throws IllegalArgumentException, IOException, FeedException {
|
||||
// need to always set the URL because this may have changed due to 3xx redirects
|
||||
syndFeedInfo.setUrl(connection.getURL());
|
||||
|
||||
// the ID is a persistant value that should stay the same even if the URL for the
|
||||
// feed changes (eg, by 3xx redirects)
|
||||
syndFeedInfo.setId(orignalUrl.toString());
|
||||
|
||||
// This will be 0 if the server doesn't support or isn't setting the last modified header
|
||||
syndFeedInfo.setLastModified(new Long(connection.getLastModified()));
|
||||
|
||||
// get the contents
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = connection.getInputStream();
|
||||
SyndFeed syndFeed = getSyndFeedFromStream(inputStream, connection);
|
||||
syndFeedInfo.setSyndFeed(syndFeed);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
private SyndFeed getSyndFeedFromStream(InputStream inputStream, URLConnection connection) throws IOException, IllegalArgumentException, FeedException {
|
||||
SyndFeed feed = readSyndFeedFromStream(inputStream, connection);
|
||||
fireEvent(FetcherEvent.EVENT_TYPE_FEED_RETRIEVED, connection, feed);
|
||||
return feed;
|
||||
}
|
||||
private SyndFeed readSyndFeedFromStream(InputStream inputStream, URLConnection connection) throws IOException, IllegalArgumentException, FeedException {
|
||||
BufferedInputStream is;
|
||||
if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) {
|
||||
// handle gzip encoded content
|
||||
is = new BufferedInputStream(new GZIPInputStream(inputStream));
|
||||
} else {
|
||||
is = new BufferedInputStream(inputStream);
|
||||
}
|
||||
|
||||
XmlReader reader = null;
|
||||
if (connection.getHeaderField("Content-Type") != null) {
|
||||
reader = new XmlReader(is, connection.getHeaderField("Content-Type"), true);
|
||||
} else {
|
||||
reader = new XmlReader(is, true);
|
||||
}
|
||||
|
||||
SyndFeedInput syndFeedInput = new SyndFeedInput();
|
||||
syndFeedInput.setPreserveWireFeed(isPreserveWireFeed());
|
||||
|
||||
return syndFeedInput.build(reader);
|
||||
}
|
||||
}
|
||||
@@ -18,31 +18,34 @@ package org.springframework.integration.feed.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Handles parsing the configuration for the feed inbound channel adapter.
|
||||
* Handles parsing the configuration for the feed inbound-channel-adapter.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FeedMessageSourceBeanDefinitionParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
public class FeedInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected String parseSource(final Element element, final ParserContext parserContext) {
|
||||
BeanDefinitionBuilder feedEntryBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.feed.FeedEntryReaderMessageSource");
|
||||
BeanDefinitionBuilder feedBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.feed.FeedReaderMessageSource");
|
||||
feedBuilder.addConstructorArgValue(element.getAttribute("feed-url"));
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(feedEntryBuilder, element, "metadata-store");
|
||||
feedEntryBuilder.addConstructorArgValue(feedBuilder.getBeanDefinition());
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(feedEntryBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
protected BeanMetadataElement parseSource(final Element element, final ParserContext parserContext) {
|
||||
BeanDefinitionBuilder sourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.feed.FeedEntryMessageSource");
|
||||
sourceBuilder.addConstructorArgValue(element.getAttribute("url"));
|
||||
String feedFetcherRef = element.getAttribute("feed-fetcher");
|
||||
if (StringUtils.hasText(feedFetcherRef)) {
|
||||
sourceBuilder.addConstructorArgReference(feedFetcherRef);
|
||||
}
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(sourceBuilder, element, "metadata-store");
|
||||
return sourceBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,19 +13,22 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.feed.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
|
||||
/**
|
||||
* NamespaceHandler for FEED module
|
||||
* NamespaceHandler for the feed module.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FeedNamespaceHandler extends NamespaceHandlerSupport {
|
||||
public class FeedNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new FeedMessageSourceBeanDefinitionParser());
|
||||
}
|
||||
}
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new FeedInboundChannelAdapterParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,64 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/integration/feed"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
xmlns:integration="http://www.springframework.org/schema/integration"
|
||||
targetNamespace="http://www.springframework.org/schema/integration/feed"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
xmlns:integration="http://www.springframework.org/schema/integration"
|
||||
targetNamespace="http://www.springframework.org/schema/integration/feed"
|
||||
elementFormDefault="qualified" attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/integration"
|
||||
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans" />
|
||||
<xsd:import namespace="http://www.springframework.org/schema/integration"
|
||||
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd" />
|
||||
|
||||
<xsd:element name="inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
This adapter takes a feed URL (either RSS or ATOM) and responds to updates.
|
||||
|
||||
Depending on whether you've set the prefer-updated-feeds-to-entries attribute
|
||||
or not, you will recieve <code>SyndFeed</code> or <code>SyndEntry</code> objects.
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:string"/>
|
||||
<xsd:attribute name="auto-startup" type="xsd:string" default="true" />
|
||||
<xsd:attribute name="channel" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.MessageChannel"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="metadata-store" use="optional" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to provide cusom implementation of 'org.springframework.integration.context.metadata.MetadataStore'
|
||||
to persist the state of the retrieved feeds to aviod duplicates between restarts.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.context.metadata.MetadataStore"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="feed-url" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to specify URL for RSS/ATOM feed
|
||||
<xsd:element name="inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
This adapter takes a feed URL (either RSS or ATOM) and responds to updates.
|
||||
Each Message will be sent to the provided channel with a feed entry as its payload.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:string" />
|
||||
<xsd:attribute name="url" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The URL for an RSS or ATOM feed.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="channel" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="feed-fetcher" use="optional" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference to a FeedFetcher instance for retrieveing Feeds from the provided URL.
|
||||
By default, the HTTP protocol is supported. For any other protocols or general
|
||||
customizations, provide a reference to a different implementation.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="com.sun.syndication.FeedFetcher" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="metadata-store" use="optional" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference to a MetadataStore instance for storing metadata associated with
|
||||
the retrieved feeds. If the implementation is persistent, it can help to
|
||||
prevent duplicates between restarts. If shared, it can help coordinate multiple
|
||||
instances of an adapter across different processes.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.context.metadata.MetadataStore" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="auto-startup" type="xsd:string" default="true" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -18,31 +18,29 @@ package org.springframework.integration.feed;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.context.metadata.PropertiesPersistingMetadataStore;
|
||||
import org.springframework.integration.store.PropertiesPersistingMetadataStore;
|
||||
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
import com.sun.syndication.feed.synd.SyndFeed;
|
||||
import com.sun.syndication.fetcher.FeedFetcher;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FeedEntryReaderMessageSourceTests {
|
||||
public class FeedEntryMessageSourceTests {
|
||||
|
||||
private final FeedFetcher feedFetcher = new FileUrlFeedFetcher();
|
||||
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
@@ -53,47 +51,36 @@ public class FeedEntryReaderMessageSourceTests {
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testFailureWhenNotInitialized() {
|
||||
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(mock(FeedReaderMessageSource.class));
|
||||
public void testFailureWhenNotInitialized() throws Exception {
|
||||
URL url = new URL("file:src/test/java/org/springframework/integration/feed/sample.rss");
|
||||
FeedEntryMessageSource feedEntrySource = new FeedEntryMessageSource(url);
|
||||
feedEntrySource.receive();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveFeedWithNoEntries() {
|
||||
FeedReaderMessageSource feedReaderSource = mock(FeedReaderMessageSource.class);
|
||||
SyndFeed feed = mock(SyndFeed.class);
|
||||
when(feedReaderSource.receiveSyndFeed()).thenReturn(feed);
|
||||
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
|
||||
public void testReceiveFeedWithNoEntries() throws Exception {
|
||||
URL url = new URL("file:src/test/java/org/springframework/integration/feed/empty.rss");
|
||||
FeedEntryMessageSource feedEntrySource = new FeedEntryMessageSource(url, this.feedFetcher);
|
||||
feedEntrySource.setBeanName("feedReader");
|
||||
feedEntrySource.afterPropertiesSet();
|
||||
assertNull(feedEntrySource.receive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveFeedWithEntriesSorted() {
|
||||
FeedReaderMessageSource feedReaderSource = mock(FeedReaderMessageSource.class);
|
||||
SyndFeed feed = mock(SyndFeed.class);
|
||||
SyndEntry entry1 = mock(SyndEntry.class);
|
||||
SyndEntry entry2 = mock(SyndEntry.class);
|
||||
when(entry1.getPublishedDate()).thenReturn(new Date(System.currentTimeMillis()));
|
||||
when(entry2.getPublishedDate()).thenReturn(new Date(System.currentTimeMillis()-10000));
|
||||
|
||||
List<SyndEntry> entries = new ArrayList<SyndEntry>();
|
||||
entries.add(entry2);
|
||||
entries.add(entry1);
|
||||
when(feed.getEntries()).thenReturn(entries);
|
||||
when(feedReaderSource.receiveSyndFeed()).thenReturn(feed);
|
||||
|
||||
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
|
||||
feedEntrySource.setComponentName("feedReader");
|
||||
feedEntrySource.afterPropertiesSet();
|
||||
Message<SyndEntry> entryMessage = feedEntrySource.receive();
|
||||
assertEquals(entry2, entryMessage.getPayload());
|
||||
entryMessage = feedEntrySource.receive();
|
||||
assertEquals(entry1, entryMessage.getPayload());
|
||||
reset(feed);
|
||||
entryMessage = feedEntrySource.receive();
|
||||
assertNull(entryMessage);
|
||||
public void testReceiveFeedWithEntriesSorted() throws Exception {
|
||||
URL url = new URL("file:src/test/java/org/springframework/integration/feed/sample.rss");
|
||||
FeedEntryMessageSource source = new FeedEntryMessageSource(url, this.feedFetcher);
|
||||
source.setComponentName("feedReader");
|
||||
source.afterPropertiesSet();
|
||||
Message<SyndEntry> message1 = source.receive();
|
||||
Message<SyndEntry> message2 = source.receive();
|
||||
Message<SyndEntry> message3 = source.receive();
|
||||
long time1 = message1.getPayload().getPublishedDate().getTime();
|
||||
long time2 = message2.getPayload().getPublishedDate().getTime();
|
||||
long time3 = message3.getPayload().getPublishedDate().getTime();
|
||||
assertTrue(time1 < time2);
|
||||
assertTrue(time2 < time3);
|
||||
assertNull(source.receive());
|
||||
}
|
||||
|
||||
// will test that last feed entry is remembered between the sessions
|
||||
@@ -101,8 +88,7 @@ public class FeedEntryReaderMessageSourceTests {
|
||||
@Test
|
||||
public void testReceiveFeedWithRealEntriesAndRepeatWithPersistentMetadataStore() throws Exception {
|
||||
URL url = new URL("file:src/test/java/org/springframework/integration/feed/sample.rss");
|
||||
FeedReaderMessageSource feedReaderSource = new FeedReaderMessageSource(url);
|
||||
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
|
||||
FeedEntryMessageSource feedEntrySource = new FeedEntryMessageSource(url, this.feedFetcher);
|
||||
feedEntrySource.setBeanName("feedReader");
|
||||
PropertiesPersistingMetadataStore metadataStore = new PropertiesPersistingMetadataStore();
|
||||
metadataStore.afterPropertiesSet();
|
||||
@@ -126,7 +112,7 @@ public class FeedEntryReaderMessageSourceTests {
|
||||
metadataStore.afterPropertiesSet();
|
||||
|
||||
// now test that what's been read is no longer retrieved
|
||||
feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
|
||||
feedEntrySource = new FeedEntryMessageSource(url, this.feedFetcher);
|
||||
feedEntrySource.setBeanName("feedReader");
|
||||
metadataStore = new PropertiesPersistingMetadataStore();
|
||||
metadataStore.afterPropertiesSet();
|
||||
@@ -142,8 +128,7 @@ public class FeedEntryReaderMessageSourceTests {
|
||||
@Test
|
||||
public void testReceiveFeedWithRealEntriesAndRepeatNoPersistentMetadataStore() throws Exception {
|
||||
URL url = new URL("file:src/test/java/org/springframework/integration/feed/sample.rss");
|
||||
FeedReaderMessageSource feedReaderSource = new FeedReaderMessageSource(url);
|
||||
FeedEntryReaderMessageSource feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
|
||||
FeedEntryMessageSource feedEntrySource = new FeedEntryMessageSource(url, this.feedFetcher);
|
||||
feedEntrySource.setBeanName("feedReader");
|
||||
feedEntrySource.afterPropertiesSet();
|
||||
SyndEntry entry1 = feedEntrySource.receive().getPayload();
|
||||
@@ -162,7 +147,7 @@ public class FeedEntryReaderMessageSourceTests {
|
||||
|
||||
// UNLIKE the previous test
|
||||
// now test that what's been read is read AGAIN
|
||||
feedEntrySource = new FeedEntryReaderMessageSource(feedReaderSource);
|
||||
feedEntrySource = new FeedEntryMessageSource(url, this.feedFetcher);
|
||||
feedEntrySource.setBeanName("feedReader");
|
||||
feedEntrySource.afterPropertiesSet();
|
||||
entry1 = feedEntrySource.receive().getPayload();
|
||||
@@ -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.feed;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.sun.syndication.feed.synd.SyndFeed;
|
||||
import com.sun.syndication.fetcher.FetcherEvent;
|
||||
import com.sun.syndication.fetcher.FetcherException;
|
||||
import com.sun.syndication.fetcher.impl.AbstractFeedFetcher;
|
||||
import com.sun.syndication.fetcher.impl.SyndFeedInfo;
|
||||
import com.sun.syndication.io.FeedException;
|
||||
import com.sun.syndication.io.SyndFeedInput;
|
||||
import com.sun.syndication.io.XmlReader;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FileUrlFeedFetcher extends AbstractFeedFetcher {
|
||||
|
||||
/**
|
||||
* Retrieve a SyndFeed for the given URL.
|
||||
* @see com.sun.syndication.fetcher.FeedFetcher#retrieveFeed(java.net.URL)
|
||||
*/
|
||||
public SyndFeed retrieveFeed(URL feedUrl) throws IOException, FeedException, FetcherException {
|
||||
Assert.notNull(feedUrl, "feedUrl must not be null");
|
||||
URLConnection connection = feedUrl.openConnection();
|
||||
SyndFeedInfo syndFeedInfo = new SyndFeedInfo();
|
||||
this.refreshFeedInfo(feedUrl, syndFeedInfo, connection);
|
||||
return syndFeedInfo.getSyndFeed();
|
||||
}
|
||||
|
||||
private void refreshFeedInfo(URL feedUrl, SyndFeedInfo syndFeedInfo, URLConnection connection) throws IOException, FeedException {
|
||||
// need to always set the URL because this may have changed due to 3xx redirects
|
||||
syndFeedInfo.setUrl(connection.getURL());
|
||||
|
||||
// the ID is a persistent value that should stay the same
|
||||
// even if the URL for the feed changes (eg, by 3xx redirects)
|
||||
syndFeedInfo.setId(feedUrl.toString());
|
||||
|
||||
// This will be 0 if the server doesn't support or isn't setting the last modified header
|
||||
syndFeedInfo.setLastModified(new Long(connection.getLastModified()));
|
||||
|
||||
// get the contents
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = connection.getInputStream();
|
||||
SyndFeed syndFeed = this.readFeedFromStream(inputStream, connection);
|
||||
syndFeedInfo.setSyndFeed(syndFeed);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SyndFeed readFeedFromStream(InputStream inputStream, URLConnection connection) throws IOException, FeedException {
|
||||
BufferedInputStream bufferedInputStream;
|
||||
if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) {
|
||||
// handle gzip encoded content
|
||||
bufferedInputStream = new BufferedInputStream(new GZIPInputStream(inputStream));
|
||||
}
|
||||
else {
|
||||
bufferedInputStream = new BufferedInputStream(inputStream);
|
||||
}
|
||||
XmlReader reader = null;
|
||||
if (connection.getHeaderField("Content-Type") != null) {
|
||||
reader = new XmlReader(bufferedInputStream, connection.getHeaderField("Content-Type"), true);
|
||||
}
|
||||
else {
|
||||
reader = new XmlReader(bufferedInputStream, true);
|
||||
}
|
||||
SyndFeedInput syndFeedInput = new SyndFeedInput();
|
||||
syndFeedInput.setPreserveWireFeed(isPreserveWireFeed());
|
||||
SyndFeed feed = syndFeedInput.build(reader);
|
||||
fireEvent(FetcherEvent.EVENT_TYPE_FEED_RETRIEVED, connection, feed);
|
||||
return feed;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-feed="http://www.springframework.org/schema/integration/feed"
|
||||
xmlns:feed="http://www.springframework.org/schema/integration/feed"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
|
||||
http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed-2.0.xsd">
|
||||
|
||||
<int-feed:inbound-channel-adapter id="feedAdapter"
|
||||
<feed:inbound-channel-adapter id="feedAdapter"
|
||||
channel="feedChannel"
|
||||
auto-startup="false"
|
||||
feed-fetcher="fileUrlFeedFetcher"
|
||||
metadata-store="customMetadataStore"
|
||||
feed-url="file:src/test/java/org/springframework/integration/feed/config/sample.rss">
|
||||
url="file:src/test/java/org/springframework/integration/feed/config/sample.rss">
|
||||
<int:poller fixed-rate="10000" max-messages-per-poll="100" />
|
||||
</int-feed:inbound-channel-adapter>
|
||||
</feed:inbound-channel-adapter>
|
||||
|
||||
<int:channel id="feedChannel">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<bean id="customMetadataStore" class="org.springframework.integration.feed.config.FeedMessageSourceBeanDefinitionParserTests.SampleMetadataStore"/>
|
||||
<bean id="fileUrlFeedFetcher" class="org.springframework.integration.feed.FileUrlFeedFetcher"/>
|
||||
|
||||
<bean id="customMetadataStore" class="org.springframework.integration.feed.config.FeedInboundChannelAdapterParserTests.SampleMetadataStore"/>
|
||||
|
||||
</beans>
|
||||
@@ -10,14 +10,17 @@
|
||||
|
||||
<int-feed:inbound-channel-adapter id="feedAdapterUsage"
|
||||
channel="feedChannelUsage"
|
||||
feed-url="file:src/test/java/org/springframework/integration/feed/config/sample.rss">
|
||||
url="file:src/test/java/org/springframework/integration/feed/config/sample.rss"
|
||||
feed-fetcher="fileUrlFeedFetcher">
|
||||
<int:poller fixed-rate="10000" max-messages-per-poll="100" fixed-delay="10000"/>
|
||||
</int-feed:inbound-channel-adapter>
|
||||
|
||||
<int:service-activator id="sampleActivator" input-channel="feedChannelUsage">
|
||||
<bean class="org.springframework.integration.feed.config.FeedMessageSourceBeanDefinitionParserTests$SampleService" />
|
||||
<bean class="org.springframework.integration.feed.config.FeedInboundChannelAdapterParserTests$SampleService" />
|
||||
</int:service-activator>
|
||||
|
||||
<bean id="metadataStore" class="org.springframework.integration.context.metadata.PropertiesPersistingMetadataStore"/>
|
||||
<bean id="fileUrlFeedFetcher" class="org.springframework.integration.feed.FileUrlFeedFetcher"/>
|
||||
|
||||
<bean id="metadataStore" class="org.springframework.integration.store.PropertiesPersistingMetadataStore"/>
|
||||
|
||||
</beans>
|
||||
@@ -7,12 +7,15 @@
|
||||
http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed-2.0.xsd">
|
||||
|
||||
<int-feed:inbound-channel-adapter channel="feedChannelUsage"
|
||||
feed-url="file:src/test/java/org/springframework/integration/feed/config/sample.rss">
|
||||
url="file:src/test/java/org/springframework/integration/feed/config/sample.rss"
|
||||
feed-fetcher="fileUrlFeedFetcher">
|
||||
<int:poller fixed-rate="10000" max-messages-per-poll="100" fixed-delay="10000"/>
|
||||
</int-feed:inbound-channel-adapter>
|
||||
|
||||
<int:service-activator id="sampleActivator" input-channel="feedChannelUsage">
|
||||
<bean class="org.springframework.integration.feed.config.FeedMessageSourceBeanDefinitionParserTests$SampleServiceNoHistory" />
|
||||
<bean class="org.springframework.integration.feed.config.FeedInboundChannelAdapterParserTests$SampleServiceNoHistory" />
|
||||
</int:service-activator>
|
||||
|
||||
<bean id="fileUrlFeedFetcher" class="org.springframework.integration.feed.FileUrlFeedFetcher"/>
|
||||
|
||||
</beans>
|
||||
@@ -8,10 +8,11 @@
|
||||
|
||||
<int-feed:inbound-channel-adapter id="feedAdapter"
|
||||
channel="feedChannel"
|
||||
auto-startup="false"
|
||||
feed-url="http://feeds.bbci.co.uk/news/rss.xml">
|
||||
url="http://feeds.bbci.co.uk/news/rss.xml"
|
||||
auto-startup="false">
|
||||
<int:poller fixed-rate="10000" max-messages-per-poll="100" />
|
||||
</int-feed:inbound-channel-adapter>
|
||||
|
||||
<int:channel id="feedChannel" />
|
||||
|
||||
</beans>
|
||||
@@ -38,13 +38,11 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.context.metadata.MetadataStore;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.feed.FeedEntryReaderMessageSource;
|
||||
import org.springframework.integration.feed.FeedReaderMessageSource;
|
||||
import org.springframework.integration.feed.FileUrlFeedFetcher;
|
||||
import org.springframework.integration.feed.FeedEntryMessageSource;
|
||||
import org.springframework.integration.history.MessageHistory;
|
||||
import org.springframework.integration.store.MetadataStore;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
|
||||
import com.sun.syndication.feed.synd.SyndEntry;
|
||||
@@ -56,7 +54,7 @@ import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FeedMessageSourceBeanDefinitionParserTests {
|
||||
public class FeedInboundChannelAdapterParserTests {
|
||||
|
||||
private static CountDownLatch latch;
|
||||
|
||||
@@ -69,23 +67,28 @@ public class FeedMessageSourceBeanDefinitionParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateSuccessfulConfigurationWithCustomMetadataStore() {
|
||||
public void validateSuccessfulFileConfigurationWithCustomMetadataStore() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"FeedMessageSourceBeanDefinitionParserTests-file-context.xml", this.getClass());
|
||||
"FeedInboundChannelAdapterParserTests-file-context.xml", this.getClass());
|
||||
SourcePollingChannelAdapter adapter = context.getBean("feedAdapter", SourcePollingChannelAdapter.class);
|
||||
FeedEntryReaderMessageSource source = (FeedEntryReaderMessageSource) TestUtils.getPropertyValue(adapter, "source");
|
||||
FeedEntryMessageSource source = (FeedEntryMessageSource) TestUtils.getPropertyValue(adapter, "source");
|
||||
MetadataStore metadataStore = (MetadataStore) TestUtils.getPropertyValue(source, "metadataStore");
|
||||
assertTrue(metadataStore instanceof SampleMetadataStore);
|
||||
assertEquals(metadataStore, context.getBean("customMetadataStore"));
|
||||
FeedReaderMessageSource feedReaderMessageSource = (FeedReaderMessageSource) TestUtils.getPropertyValue(source, "feedReaderMessageSource");
|
||||
AbstractFeedFetcher fetcher = (AbstractFeedFetcher) TestUtils.getPropertyValue(feedReaderMessageSource, "fetcher");
|
||||
assertTrue(fetcher instanceof FileUrlFeedFetcher);
|
||||
context = new ClassPathXmlApplicationContext(
|
||||
"FeedMessageSourceBeanDefinitionParserTests-http-context.xml", this.getClass());
|
||||
adapter = context.getBean("feedAdapter", SourcePollingChannelAdapter.class);
|
||||
source = (FeedEntryReaderMessageSource) TestUtils.getPropertyValue(adapter, "source");
|
||||
feedReaderMessageSource = (FeedReaderMessageSource) TestUtils.getPropertyValue(source, "feedReaderMessageSource");
|
||||
fetcher = (AbstractFeedFetcher) TestUtils.getPropertyValue(feedReaderMessageSource, "fetcher");
|
||||
AbstractFeedFetcher fetcher = (AbstractFeedFetcher) TestUtils.getPropertyValue(source, "feedFetcher");
|
||||
assertEquals("FileUrlFeedFetcher", fetcher.getClass().getSimpleName());
|
||||
context.destroy();
|
||||
}
|
||||
|
||||
public void validateSuccessfulHttpConfigurationWithCustomMetadataStore() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"FeedInboundChannelAdapterParserTests-http-context.xml", this.getClass());
|
||||
SourcePollingChannelAdapter adapter = context.getBean("feedAdapter", SourcePollingChannelAdapter.class);
|
||||
FeedEntryMessageSource source = (FeedEntryMessageSource) TestUtils.getPropertyValue(adapter, "source");
|
||||
MetadataStore metadataStore = (MetadataStore) TestUtils.getPropertyValue(source, "metadataStore");
|
||||
assertTrue(metadataStore instanceof SampleMetadataStore);
|
||||
assertEquals(metadataStore, context.getBean("customMetadataStore"));
|
||||
AbstractFeedFetcher fetcher = (AbstractFeedFetcher) TestUtils.getPropertyValue(source, "feedFetcher");
|
||||
assertTrue(fetcher instanceof HttpURLFeedFetcher);
|
||||
context.destroy();
|
||||
}
|
||||
@@ -99,7 +102,7 @@ public class FeedMessageSourceBeanDefinitionParserTests {
|
||||
//Test file samples.rss has 3 news items
|
||||
latch = spy(new CountDownLatch(3));
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"FeedMessageSourceBeanDefinitionParserTests-file-usage-context.xml", this.getClass());
|
||||
"FeedInboundChannelAdapterParserTests-file-usage-context.xml", this.getClass());
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
verify(latch, times(3)).countDown();
|
||||
context.destroy();
|
||||
@@ -108,7 +111,7 @@ public class FeedMessageSourceBeanDefinitionParserTests {
|
||||
// in this iteration no new feeds will be received and the latch will timeout
|
||||
latch = spy(new CountDownLatch(3));
|
||||
context = new ClassPathXmlApplicationContext(
|
||||
"FeedMessageSourceBeanDefinitionParserTests-file-usage-context.xml", this.getClass());
|
||||
"FeedInboundChannelAdapterParserTests-file-usage-context.xml", this.getClass());
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
verify(latch, times(0)).countDown();
|
||||
context.destroy();
|
||||
@@ -119,7 +122,7 @@ public class FeedMessageSourceBeanDefinitionParserTests {
|
||||
//Test file samples.rss has 3 news items
|
||||
latch = spy(new CountDownLatch(3));
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"FeedMessageSourceBeanDefinitionParserTests-file-usage-noid-context.xml", this.getClass());
|
||||
"FeedInboundChannelAdapterParserTests-file-usage-noid-context.xml", this.getClass());
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
verify(latch, times(3)).countDown();
|
||||
context.destroy();
|
||||
@@ -128,7 +131,7 @@ public class FeedMessageSourceBeanDefinitionParserTests {
|
||||
// in this iteration no new feeds will be received and the latch will timeout
|
||||
latch = spy(new CountDownLatch(3));
|
||||
context = new ClassPathXmlApplicationContext(
|
||||
"FeedMessageSourceBeanDefinitionParserTests-file-usage-noid-context.xml", this.getClass());
|
||||
"FeedInboundChannelAdapterParserTests-file-usage-noid-context.xml", this.getClass());
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
verify(latch, times(3)).countDown();
|
||||
context.destroy();
|
||||
@@ -144,7 +147,7 @@ public class FeedMessageSourceBeanDefinitionParserTests {
|
||||
}
|
||||
});
|
||||
ApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"FeedMessageSourceBeanDefinitionParserTests-http-context.xml", this.getClass());
|
||||
"FeedInboundChannelAdapterParserTests-http-context.xml", this.getClass());
|
||||
DirectChannel feedChannel = context.getBean("feedChannel", DirectChannel.class);
|
||||
feedChannel.subscribe(handler);
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
@@ -0,0 +1,20 @@
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>Spring Integration</title>
|
||||
<link>http://www.springsource.org/spring-integration</link>
|
||||
<description>
|
||||
Spring Integration is a really cool framework
|
||||
</description>
|
||||
<language>en-us</language>
|
||||
<copyright>Copyright 2004-2010 SpringSource/VMWare
|
||||
All Rights Reserved.</copyright>
|
||||
<lastBuildDate>Tue, 12 Apr 2010 18:21:32 EST</lastBuildDate>
|
||||
<ttl>240</ttl>
|
||||
<image>
|
||||
<url>http://www.springsource.org/sites/all/themes/dotorg09/images/dotorg09_logo.png</url>
|
||||
<title>Spring Integration</title>
|
||||
<link>http://www.springsource.org/spring-integration</link>
|
||||
</image>
|
||||
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
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.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
@@ -24,7 +28,6 @@ import org.springframework.integration.config.xml.AbstractPollingInboundChannelA
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for the <inbound-channel-adapter> element of the 'file' namespace.
|
||||
@@ -38,7 +41,7 @@ public class FileInboundChannelAdapterParser extends AbstractPollingInboundChann
|
||||
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
PACKAGE_NAME + ".config.FileReadingMessageSourceFactoryBean");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "comparator");
|
||||
@@ -52,7 +55,8 @@ public class FileInboundChannelAdapterParser extends AbstractPollingInboundChann
|
||||
builder.addPropertyReference("locker", lockerBeanName);
|
||||
}
|
||||
builder.addPropertyReference("filter", filterBeanName);
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
String beanName = BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return new RuntimeBeanReference(beanName);
|
||||
}
|
||||
|
||||
private String registerLocker(Element element, ParserContext parserContext) {
|
||||
|
||||
@@ -1,40 +1,65 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPClientConfig;
|
||||
import org.apache.commons.net.ftp.FTPReply;
|
||||
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* base class for the other {@link org.springframework.integration.ftp.FtpClientFactory} implementations.
|
||||
* Most of this came out of the {@link DefaultFtpClientFactory} and was refactored into a base class
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
abstract public class AbstractFtpClientFactory<T extends FTPClient> implements FtpClientFactory<T> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FtpClientFactory.class);
|
||||
|
||||
private static final String DEFAULT_REMOTE_WORKING_DIRECTORY = "/";
|
||||
|
||||
|
||||
protected FTPClientConfig config;
|
||||
|
||||
protected String username;
|
||||
|
||||
protected String host;
|
||||
|
||||
protected String password;
|
||||
|
||||
protected int port = FTP.DEFAULT_PORT;
|
||||
|
||||
protected String remoteWorkingDirectory = DEFAULT_REMOTE_WORKING_DIRECTORY;
|
||||
|
||||
protected int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE;
|
||||
|
||||
protected int fileType = FTP.BINARY_FILE_TYPE;
|
||||
|
||||
|
||||
public void setFileType(int fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
@@ -70,9 +95,9 @@ abstract public class AbstractFtpClientFactory<T extends FTPClient> implements F
|
||||
}
|
||||
|
||||
/**
|
||||
* Set client mode for example
|
||||
* <code>FTPClient.ACTIVE_LOCAL_CONNECTION_MODE</code> (default) Only local
|
||||
* modes are supported.
|
||||
* Set client mode, for example
|
||||
* <code>FTPClient.ACTIVE_LOCAL_CONNECTION_MODE</code> (default)
|
||||
* Only local modes are supported.
|
||||
*/
|
||||
public void setClientMode(int clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
@@ -118,7 +143,6 @@ abstract public class AbstractFtpClientFactory<T extends FTPClient> implements F
|
||||
}
|
||||
|
||||
setClientMode(client);
|
||||
|
||||
client.setFileType(this.fileType);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -135,7 +159,6 @@ abstract public class AbstractFtpClientFactory<T extends FTPClient> implements F
|
||||
logger.debug("working directory is: " +
|
||||
client.printWorkingDirectory());
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -146,16 +169,13 @@ abstract public class AbstractFtpClientFactory<T extends FTPClient> implements F
|
||||
switch (clientMode) {
|
||||
case FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE:
|
||||
client.enterLocalActiveMode();
|
||||
|
||||
break;
|
||||
|
||||
case FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE:
|
||||
client.enterLocalPassiveMode();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -5,91 +21,78 @@ import org.springframework.util.StringUtils;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
|
||||
/**
|
||||
* Factors out the client factory creaton
|
||||
* Factors out the client factory creation.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class ClientFactorySupport {
|
||||
public static DefaultFtpsClientFactory ftpsClientFactory(String host,
|
||||
int port, String remoteDir, String user, String pw, int fileType,
|
||||
|
||||
public static DefaultFtpsClientFactory ftpsClientFactory(String host, int port, String remoteWorkingDirectory, String user, String password, int fileType,
|
||||
int clientMode, String prot, String protocol, String authValue,
|
||||
Boolean implicit, TrustManager trustManager, KeyManager keyManager,
|
||||
Boolean sessionCreation, Boolean useClientMode,
|
||||
Boolean wantsClientAuth, Boolean needClientAuth, String[] cipherSuites) {
|
||||
DefaultFtpsClientFactory defaultFtpClientFactory = new DefaultFtpsClientFactory();
|
||||
defaultFtpClientFactory.setHost(host);
|
||||
defaultFtpClientFactory.setPassword(pw);
|
||||
defaultFtpClientFactory.setPort((port));
|
||||
defaultFtpClientFactory.setRemoteWorkingDirectory(remoteDir);
|
||||
defaultFtpClientFactory.setPassword(password);
|
||||
defaultFtpClientFactory.setPort(port);
|
||||
defaultFtpClientFactory.setRemoteWorkingDirectory(remoteWorkingDirectory);
|
||||
defaultFtpClientFactory.setUsername(user);
|
||||
defaultFtpClientFactory.setFileType(fileType);
|
||||
defaultFtpClientFactory.setClientMode(clientMode);
|
||||
|
||||
if (cipherSuites != null) {
|
||||
defaultFtpClientFactory.setCipherSuites(cipherSuites);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(prot)) {
|
||||
defaultFtpClientFactory.setProt(prot);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(protocol)) {
|
||||
defaultFtpClientFactory.setProtocol(protocol);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(authValue)) {
|
||||
defaultFtpClientFactory.setAuthValue(authValue);
|
||||
}
|
||||
|
||||
if (null != implicit) {
|
||||
defaultFtpClientFactory.setImplicit(implicit);
|
||||
}
|
||||
|
||||
if (trustManager != null) {
|
||||
defaultFtpClientFactory.setTrustManager(trustManager);
|
||||
}
|
||||
|
||||
if (keyManager != null) {
|
||||
defaultFtpClientFactory.setKeyManager(keyManager);
|
||||
}
|
||||
|
||||
if (needClientAuth != null) {
|
||||
defaultFtpClientFactory.setNeedClientAuth(needClientAuth);
|
||||
}
|
||||
|
||||
if (wantsClientAuth != null) {
|
||||
defaultFtpClientFactory.setWantsClientAuth(wantsClientAuth);
|
||||
}
|
||||
|
||||
if (sessionCreation != null) {
|
||||
defaultFtpClientFactory.setSessionCreation(sessionCreation);
|
||||
}
|
||||
|
||||
if (useClientMode != null) {
|
||||
defaultFtpClientFactory.setUseClientMode(useClientMode);
|
||||
}
|
||||
|
||||
return defaultFtpClientFactory;
|
||||
}
|
||||
|
||||
public static DefaultFtpClientFactory ftpClientFactory(String host,
|
||||
int port,
|
||||
String remoteDir,
|
||||
String remoteWorkingDirectory,
|
||||
String user,
|
||||
String pw,
|
||||
String password,
|
||||
int clientMode,
|
||||
int fileType) {
|
||||
DefaultFtpClientFactory defaultFtpClientFactory = new DefaultFtpClientFactory();
|
||||
defaultFtpClientFactory.setHost(host);
|
||||
defaultFtpClientFactory.setPassword(pw);
|
||||
defaultFtpClientFactory.setPassword(password);
|
||||
defaultFtpClientFactory.setPort(port);
|
||||
defaultFtpClientFactory.setRemoteWorkingDirectory(remoteDir);
|
||||
defaultFtpClientFactory.setRemoteWorkingDirectory(remoteWorkingDirectory);
|
||||
defaultFtpClientFactory.setUsername(user);
|
||||
defaultFtpClientFactory.setClientMode(clientMode);
|
||||
defaultFtpClientFactory.setFileType(fileType);
|
||||
|
||||
return defaultFtpClientFactory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -13,20 +13,22 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of FtpClientFactory.
|
||||
*
|
||||
* @author iwein
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class DefaultFtpClientFactory extends AbstractFtpClientFactory<FTPClient> {
|
||||
|
||||
@Override
|
||||
protected FTPClient createSingleInstanceOfClient() {
|
||||
return new FTPClient();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +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.ftp;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPSClient;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.SocketException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPSClient;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* provides a working FTPS implementation. Based heavily on {@link org.springframework.integration.ftp.DefaultFtpClientFactory}
|
||||
@@ -25,19 +34,32 @@ import java.util.Properties;
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class DefaultFtpsClientFactory extends AbstractFtpClientFactory<FTPSClient> {
|
||||
|
||||
private Boolean useClientMode;
|
||||
|
||||
private Boolean sessionCreation;
|
||||
|
||||
private String authValue;
|
||||
|
||||
private TrustManager trustManager;
|
||||
|
||||
private String[] cipherSuites;
|
||||
|
||||
private String[] protocols;
|
||||
|
||||
private KeyManager keyManager;
|
||||
|
||||
private Boolean needClientAuth;
|
||||
|
||||
private Boolean wantsClientAuth;
|
||||
|
||||
private boolean implicit = false;
|
||||
|
||||
private String prot = "P";
|
||||
|
||||
private String protocol;
|
||||
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
@@ -82,9 +104,12 @@ public class DefaultFtpsClientFactory extends AbstractFtpClientFactory<FTPSClien
|
||||
this.prot = prot;
|
||||
}
|
||||
|
||||
public void setImplicit(boolean implicit) {
|
||||
this.implicit = implicit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAfterConnect(FTPSClient ftpsClient)
|
||||
throws IOException {
|
||||
protected void onAfterConnect(FTPSClient ftpsClient) throws IOException {
|
||||
ftpsClient.execPBSZ(0);
|
||||
ftpsClient.execPROT(this.prot);
|
||||
}
|
||||
@@ -92,64 +117,50 @@ public class DefaultFtpsClientFactory extends AbstractFtpClientFactory<FTPSClien
|
||||
@Override
|
||||
public FTPSClient getClient() throws SocketException, IOException {
|
||||
FTPSClient ftpsClient = super.getClient();
|
||||
|
||||
if (StringUtils.hasText(this.authValue)) {
|
||||
ftpsClient.setAuthValue(authValue);
|
||||
}
|
||||
|
||||
if (this.trustManager != null) {
|
||||
ftpsClient.setTrustManager(this.trustManager);
|
||||
}
|
||||
|
||||
if (this.cipherSuites != null) {
|
||||
ftpsClient.setEnabledCipherSuites(this.cipherSuites);
|
||||
}
|
||||
|
||||
if (this.protocols != null) {
|
||||
ftpsClient.setEnabledProtocols(this.protocols);
|
||||
}
|
||||
|
||||
if (this.sessionCreation != null) {
|
||||
ftpsClient.setEnabledSessionCreation(this.sessionCreation);
|
||||
}
|
||||
|
||||
if (this.useClientMode != null) {
|
||||
ftpsClient.setUseClientMode(this.useClientMode);
|
||||
}
|
||||
|
||||
if (this.sessionCreation != null) {
|
||||
ftpsClient.setEnabledSessionCreation(this.sessionCreation);
|
||||
}
|
||||
|
||||
if (this.keyManager != null) {
|
||||
ftpsClient.setKeyManager(keyManager);
|
||||
}
|
||||
|
||||
if (this.needClientAuth != null) {
|
||||
ftpsClient.setNeedClientAuth(this.needClientAuth);
|
||||
}
|
||||
|
||||
if (this.wantsClientAuth != null) {
|
||||
ftpsClient.setWantClientAuth(this.wantsClientAuth);
|
||||
}
|
||||
|
||||
return ftpsClient;
|
||||
}
|
||||
|
||||
public void setImplicit(boolean implicit) {
|
||||
this.implicit = implicit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FTPSClient createSingleInstanceOfClient() {
|
||||
try {
|
||||
if (StringUtils.hasText(this.protocol)) {
|
||||
return new FTPSClient(this.protocol, this.implicit);
|
||||
}
|
||||
|
||||
return new FTPSClient(this.implicit);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -13,22 +13,24 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Factory for {@link FTPClient}.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public interface FtpClientFactory<T extends FTPClient> {
|
||||
|
||||
/**
|
||||
* @return Fully configured and connected FTPClient. Never <code>null</code>.
|
||||
* @throws IOException thrown when a networking IO subsystem error occurs
|
||||
*/
|
||||
T getClient() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -13,11 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
|
||||
|
||||
/**
|
||||
* A pool of {@link FTPClient} instances. The pool can be used to control the
|
||||
* number of open FTP connections and reuse these connections efficiently.
|
||||
@@ -25,6 +25,7 @@ import org.apache.commons.net.ftp.FTPClient;
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public interface FtpClientPool extends FtpClientFactory {
|
||||
|
||||
/**
|
||||
* Releases the client back to the pool. When calling this method the caller
|
||||
* is no longer responsible for the connection. The pool is free to do with
|
||||
@@ -43,4 +44,5 @@ public interface FtpClientPool extends FtpClientFactory {
|
||||
* <code>null</code>.
|
||||
*/
|
||||
void releaseClient(FTPClient client);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,33 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.springframework.integration.file.entries.EntryNamer;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.integration.file.entries.EntryNamer} for {@link org.apache.commons.net.ftp.FTPFile} objects
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpFileEntryNamer implements EntryNamer<FTPFile> {
|
||||
|
||||
public String nameOf(FTPFile entry) {
|
||||
return entry.getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
@@ -17,24 +33,22 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* An FTP-adapter implementation of {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer}
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpInboundRemoteFileSystemSynchronizer
|
||||
extends AbstractInboundRemoteFileSystemSychronizer<FTPFile> {
|
||||
protected FtpClientPool clientPool;
|
||||
public class FtpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemoteFileSystemSychronizer<FTPFile> {
|
||||
|
||||
private volatile Trigger trigger = new PeriodicTrigger(10 * 1000);
|
||||
|
||||
protected volatile FtpClientPool clientPool;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.clientPool, "clientPool can't be null");
|
||||
|
||||
if (this.shouldDeleteSourceFile) {
|
||||
this.entryAcknowledgmentStrategy = new DeletionEntryAcknowledgmentStrategy();
|
||||
}
|
||||
protected Trigger getTrigger() {
|
||||
return this.trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,37 +60,40 @@ public class FtpInboundRemoteFileSystemSynchronizer
|
||||
this.clientPool = clientPool;
|
||||
}
|
||||
|
||||
protected boolean copyFileToLocalDirectory(FTPClient client,
|
||||
FTPFile ftpFile, Resource localDirectory)
|
||||
throws IOException, FileNotFoundException {
|
||||
String remoteFileName = ftpFile.getName();
|
||||
String localFileName = localDirectory.getFile().getPath() + "/" +
|
||||
remoteFileName;
|
||||
File localFile = new File(localFileName);
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.clientPool, "clientPool must not be null");
|
||||
if (this.shouldDeleteSourceFile) {
|
||||
this.entryAcknowledgmentStrategy = new DeletionEntryAcknowledgmentStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean copyFileToLocalDirectory(FTPClient client, FTPFile ftpFile, Resource localDirectory)
|
||||
throws IOException, FileNotFoundException {
|
||||
|
||||
String remoteFileName = ftpFile.getName();
|
||||
String localFileName = localDirectory.getFile().getPath() + "/" + remoteFileName;
|
||||
File localFile = new File(localFileName);
|
||||
if (!localFile.exists()) {
|
||||
String tempFileName = localFileName +
|
||||
AbstractInboundRemoteFileSystemSynchronizingMessageSource.INCOMPLETE_EXTENSION;
|
||||
File file = new File(tempFileName);
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
|
||||
try {
|
||||
client.retrieveFile(remoteFileName, fos);
|
||||
|
||||
// Perhaps we have some dispatch of hte source file to do?
|
||||
// Perhaps we have some dispatch of the source file to do?
|
||||
acknowledge(client, ftpFile);
|
||||
} catch (Throwable th) {
|
||||
}
|
||||
catch (Throwable th) {
|
||||
throw new RuntimeException(th);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
fos.close();
|
||||
}
|
||||
|
||||
file.renameTo(localFile);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,44 +103,38 @@ public class FtpInboundRemoteFileSystemSynchronizer
|
||||
Assert.state(client != null,
|
||||
FtpClientPool.class.getSimpleName() +
|
||||
" returned a 'null' client. " +
|
||||
"This most likely a bug in the pool implementation.");
|
||||
|
||||
"This is most likely a bug in the pool implementation.");
|
||||
Collection<FTPFile> fileList = this.filter.filterEntries(client.listFiles());
|
||||
|
||||
try {
|
||||
for (FTPFile ftpFile : fileList) {
|
||||
if ((ftpFile != null) && ftpFile.isFile()) {
|
||||
copyFileToLocalDirectory(client, ftpFile,
|
||||
this.localDirectory);
|
||||
copyFileToLocalDirectory(client, ftpFile, this.localDirectory);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
this.clientPool.releaseClient(client);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MessagingException("Problem occurred while synchronizing remote to local directory",
|
||||
e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessagingException("Problem occurred while synchronizing remote to local directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Trigger getTrigger() {
|
||||
return new PeriodicTrigger(10 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* An ackowledgment strategy that deletes
|
||||
* An acknowledgment strategy that deletes the file.
|
||||
*/
|
||||
class DeletionEntryAcknowledgmentStrategy implements AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy<FTPFile> {
|
||||
public void acknowledge(Object useful, FTPFile msg)
|
||||
throws Exception {
|
||||
FTPClient ftpClient = (FTPClient) useful;
|
||||
private class DeletionEntryAcknowledgmentStrategy implements AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy<FTPFile> {
|
||||
|
||||
if ((msg != null) && ftpClient.deleteFile(msg.getName())) {
|
||||
public void acknowledge(Object useful, FTPFile fptFile) throws Exception {
|
||||
FTPClient ftpClient = (FTPClient) useful;
|
||||
if ((fptFile != null) && ftpClient.deleteFile(fptFile.getName())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("deleted " + msg.getName());
|
||||
logger.debug("deleted " + fptFile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,51 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource;
|
||||
|
||||
|
||||
/**
|
||||
* a {@link org.springframework.integration.core.MessageSource} implementation for FTP
|
||||
* A {@link org.springframework.integration.core.MessageSource} implementation for FTP.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpInboundRemoteFileSystemSynchronizingMessageSource
|
||||
extends AbstractInboundRemoteFileSystemSynchronizingMessageSource<FTPFile, FtpInboundRemoteFileSystemSynchronizer> {
|
||||
|
||||
private volatile FtpClientPool clientPool;
|
||||
|
||||
|
||||
public void setClientPool(FtpClientPool clientPool) {
|
||||
this.clientPool = clientPool;
|
||||
}
|
||||
|
||||
public String getComponentType() {
|
||||
return "ftp:inbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
this.synchronizer.setClientPool(this.clientPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
this.synchronizer.start();
|
||||
@@ -28,13 +56,4 @@ public class FtpInboundRemoteFileSystemSynchronizingMessageSource
|
||||
this.synchronizer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
this.synchronizer.setClientPool(this.clientPool);
|
||||
}
|
||||
|
||||
public String getComponentType() {
|
||||
return "ftp:inbound-channel-adapter";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -14,9 +33,6 @@ import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.entries.PatternMatchingEntryListFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* Factory to make building the namespace easier
|
||||
*
|
||||
@@ -24,35 +40,91 @@ import java.io.File;
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean
|
||||
extends AbstractFactoryBean<FtpInboundRemoteFileSystemSynchronizingMessageSource>
|
||||
implements ResourceLoaderAware {
|
||||
extends AbstractFactoryBean<FtpInboundRemoteFileSystemSynchronizingMessageSource> implements ResourceLoaderAware {
|
||||
|
||||
protected volatile String port;
|
||||
|
||||
protected volatile String autoCreateDirectories;
|
||||
|
||||
protected volatile String filenamePattern;
|
||||
|
||||
protected volatile String username;
|
||||
|
||||
protected volatile String password;
|
||||
|
||||
protected volatile String host;
|
||||
|
||||
protected volatile String remoteDirectory;
|
||||
|
||||
protected volatile String localWorkingDirectory;
|
||||
|
||||
protected volatile ResourceLoader resourceLoader;
|
||||
|
||||
protected volatile Resource localDirectoryResource;
|
||||
|
||||
protected volatile EntryListFilter<FTPFile> filter;
|
||||
|
||||
protected volatile int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE;
|
||||
|
||||
protected volatile int fileType = FTP.BINARY_FILE_TYPE;
|
||||
|
||||
private volatile String autoDeleteRemoteFilesOnSync;
|
||||
|
||||
protected String defaultFtpInboundFolderName = "ftpInbound";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
public void setFileType(int fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoDeleteRemoteFilesOnSync(
|
||||
String autoDeleteRemoteFilesOnSync) {
|
||||
public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) {
|
||||
this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync;
|
||||
}
|
||||
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setAutoCreateDirectories(String autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
|
||||
public void setLocalWorkingDirectory(String localWorkingDirectory) {
|
||||
this.localWorkingDirectory = localWorkingDirectory;
|
||||
}
|
||||
|
||||
public void setFilter(EntryListFilter<FTPFile> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public void setClientMode(int clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return FtpInboundRemoteFileSystemSynchronizingMessageSource.class;
|
||||
@@ -61,122 +133,52 @@ public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean
|
||||
private Resource fromText(String path) {
|
||||
ResourceEditor resourceEditor = new ResourceEditor(this.resourceLoader);
|
||||
resourceEditor.setAsText(path);
|
||||
|
||||
return (Resource) resourceEditor.getValue();
|
||||
}
|
||||
|
||||
protected AbstractFtpClientFactory defaultClientFactory()
|
||||
throws Exception {
|
||||
protected AbstractFtpClientFactory<?> defaultClientFactory() throws Exception {
|
||||
return ClientFactorySupport.ftpClientFactory(this.host,
|
||||
Integer.parseInt(this.port), this.remoteDirectory, this.username,
|
||||
this.password, this.clientMode, this.fileType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FtpInboundRemoteFileSystemSynchronizingMessageSource createInstance()
|
||||
throws Exception {
|
||||
protected FtpInboundRemoteFileSystemSynchronizingMessageSource createInstance() throws Exception {
|
||||
boolean autoCreatDirs = Boolean.parseBoolean(this.autoCreateDirectories);
|
||||
boolean ackRemoteDir = Boolean.parseBoolean(this.autoDeleteRemoteFilesOnSync);
|
||||
|
||||
FtpInboundRemoteFileSystemSynchronizingMessageSource ftpRemoteFileSystemSynchronizingMessageSource =
|
||||
new FtpInboundRemoteFileSystemSynchronizingMessageSource();
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setAutoCreateDirectories(autoCreatDirs);
|
||||
|
||||
if (!StringUtils.hasText(this.localWorkingDirectory)) {
|
||||
File tmp = new File(SystemUtils.getJavaIoTmpDir(), defaultFtpInboundFolderName);
|
||||
this.localWorkingDirectory = "file://" + tmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
this.localDirectoryResource = this.fromText(this.localWorkingDirectory);
|
||||
|
||||
FtpFileEntryNamer ftpFileEntryNamer = new FtpFileEntryNamer();
|
||||
CompositeEntryListFilter<FTPFile> compositeFtpFileListFilter = new CompositeEntryListFilter<FTPFile>();
|
||||
|
||||
if (StringUtils.hasText(this.filenamePattern)) {
|
||||
PatternMatchingEntryListFilter<FTPFile> ftpFilePatternMatchingEntryListFilter =
|
||||
new PatternMatchingEntryListFilter<FTPFile>(ftpFileEntryNamer, filenamePattern);
|
||||
compositeFtpFileListFilter.addFilter(ftpFilePatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
if (this.filter != null) {
|
||||
compositeFtpFileListFilter.addFilter(this.filter);
|
||||
}
|
||||
|
||||
QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15,
|
||||
defaultClientFactory());
|
||||
|
||||
QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, defaultClientFactory());
|
||||
FtpInboundRemoteFileSystemSynchronizer ftpRemoteFileSystemSynchronizer = new FtpInboundRemoteFileSystemSynchronizer();
|
||||
ftpRemoteFileSystemSynchronizer.setClientPool(queuedFtpClientPool);
|
||||
ftpRemoteFileSystemSynchronizer.setLocalDirectory(this.localDirectoryResource);
|
||||
ftpRemoteFileSystemSynchronizer.setShouldDeleteSourceFile(ackRemoteDir);
|
||||
|
||||
ftpRemoteFileSystemSynchronizer.setFilter(compositeFtpFileListFilter);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setRemotePredicate(compositeFtpFileListFilter);
|
||||
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setSynchronizer(ftpRemoteFileSystemSynchronizer);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setClientPool(queuedFtpClientPool);
|
||||
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setLocalDirectory(this.localDirectoryResource);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setBeanFactory(this.getBeanFactory());
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.setAutoStartup(true);
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.afterPropertiesSet();
|
||||
ftpRemoteFileSystemSynchronizingMessageSource.start();
|
||||
|
||||
return ftpRemoteFileSystemSynchronizingMessageSource;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoCreateDirectories(String autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setLocalWorkingDirectory(String localWorkingDirectory) {
|
||||
this.localWorkingDirectory = localWorkingDirectory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilter(EntryListFilter<FTPFile> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setClientMode(int clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,14 +85,12 @@ public class FtpSendingMessageHandler extends AbstractMessageHandler{
|
||||
}
|
||||
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(ftpClientPool, "'ftpClientPool' must not be null");
|
||||
Assert.notNull(temporaryBufferFolder,
|
||||
Assert.notNull(this.ftpClientPool, "'ftpClientPool' must not be null");
|
||||
Assert.notNull(this.temporaryBufferFolder,
|
||||
"'temporaryBufferFolder' must not be null");
|
||||
temporaryBufferFolderFile = this.temporaryBufferFolder.getFile();
|
||||
this.temporaryBufferFolderFile = this.temporaryBufferFolder.getFile();
|
||||
}
|
||||
|
||||
/* Ugh this needs to be put in a convenient place accessible for all the file:, sftp:, and ftp:* adapters */
|
||||
|
||||
private File handleFileMessage(File sourceFile, File tempFile, File resultFile) throws IOException {
|
||||
if (sourceFile.renameTo(resultFile)) {
|
||||
return resultFile;
|
||||
@@ -108,8 +106,7 @@ public class FtpSendingMessageHandler extends AbstractMessageHandler{
|
||||
return resultFile;
|
||||
}
|
||||
|
||||
private File handleStringMessage(String content, File tempFile,
|
||||
File resultFile, String charset) throws IOException {
|
||||
private File handleStringMessage(String content, File tempFile, File resultFile, String charset) throws IOException {
|
||||
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile), charset);
|
||||
FileCopyUtils.copy(content, writer);
|
||||
tempFile.renameTo(resultFile);
|
||||
@@ -162,25 +159,21 @@ public class FtpSendingMessageHandler extends AbstractMessageHandler{
|
||||
Assert.notNull(message, "'message' must not be null");
|
||||
Object payload = message.getPayload();
|
||||
Assert.notNull(payload, "Message payload must not be null");
|
||||
|
||||
File file = this.redeemForStorableFile(message);
|
||||
|
||||
if ((file != null) && file.exists()) {
|
||||
FTPClient client = null;
|
||||
boolean sentSuccesfully;
|
||||
try {
|
||||
client = getFtpClient();
|
||||
sentSuccesfully = sendFile(file, client);
|
||||
} catch (FileNotFoundException e) {
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"File [" + file +
|
||||
"] not found in local working directory; it was moved or deleted unexpectedly",
|
||||
e);
|
||||
"File [" + file + "] not found in local working directory; it was moved or deleted unexpectedly", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"Error transferring file [" + file +
|
||||
"] from local working directory to remote FTP directory", e);
|
||||
"Error transferring file [" + file + "] from local working directory to remote FTP directory", e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageDeliveryException(message,
|
||||
@@ -190,8 +183,9 @@ public class FtpSendingMessageHandler extends AbstractMessageHandler{
|
||||
if (file.exists()) {
|
||||
try {
|
||||
file.delete();
|
||||
} catch (Throwable th) {
|
||||
/// noop
|
||||
}
|
||||
catch (Throwable th) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (client != null) {
|
||||
@@ -199,8 +193,7 @@ public class FtpSendingMessageHandler extends AbstractMessageHandler{
|
||||
}
|
||||
}
|
||||
if (!sentSuccesfully) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"Failed to store file '" + file + "'");
|
||||
throw new MessageDeliveryException(message, "Failed to store file '" + file + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -10,100 +26,106 @@ import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
|
||||
|
||||
/**
|
||||
* A factory bean implementation that handles constructing an outbound FTP adapter.
|
||||
*
|
||||
* A factory bean implementation that handles constructing an outbound FTP
|
||||
* adapter.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpSendingMessageHandlerFactoryBean extends AbstractFactoryBean<FtpSendingMessageHandler>
|
||||
implements ResourceLoaderAware, ApplicationContextAware {
|
||||
protected int port;
|
||||
protected String username;
|
||||
protected String password;
|
||||
protected String host;
|
||||
protected String remoteDirectory;
|
||||
private String charset;
|
||||
protected int clientMode;
|
||||
private int fileType;
|
||||
implements ResourceLoaderAware, ApplicationContextAware {
|
||||
|
||||
protected int port;
|
||||
|
||||
protected String username;
|
||||
|
||||
protected String password;
|
||||
|
||||
protected String host;
|
||||
|
||||
protected String remoteDirectory;
|
||||
|
||||
private String charset;
|
||||
|
||||
protected int clientMode;
|
||||
|
||||
private int fileType;
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private FileNameGenerator fileNameGenerator;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public void setFileNameGenerator(FileNameGenerator fileNameGenerator) {
|
||||
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public void setFileNameGenerator(FileNameGenerator fileNameGenerator) {
|
||||
this.fileNameGenerator = fileNameGenerator;
|
||||
}
|
||||
|
||||
public void setFileType(int fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
public void setFileType(int fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public void setClientMode(int clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
}
|
||||
public void setClientMode(int clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?extends FtpSendingMessageHandler> getObjectType() {
|
||||
return FtpSendingMessageHandler.class;
|
||||
}
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
protected AbstractFtpClientFactory clientFactory() {
|
||||
return ClientFactorySupport.ftpClientFactory(this.host, this.port,
|
||||
this.remoteDirectory, this.username, this.password,
|
||||
this.clientMode, this.fileType);
|
||||
}
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FtpSendingMessageHandler createInstance()
|
||||
throws Exception {
|
||||
// the dependencies for the outbound-adapter are much simpler
|
||||
// they only require an instance of the pool
|
||||
AbstractFtpClientFactory defaultFtpClientFactory = clientFactory();
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
|
||||
QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15,
|
||||
defaultFtpClientFactory);
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
FtpSendingMessageHandler ftpSendingMessageHandler = new FtpSendingMessageHandler(queuedFtpClientPool);
|
||||
ftpSendingMessageHandler.setFileNameGenerator(this.fileNameGenerator);
|
||||
if (this.charset != null) {
|
||||
ftpSendingMessageHandler.setCharset(this.charset);
|
||||
}
|
||||
ftpSendingMessageHandler.afterPropertiesSet();
|
||||
return ftpSendingMessageHandler;
|
||||
}
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
@Override
|
||||
public Class<? extends FtpSendingMessageHandler> getObjectType() {
|
||||
return FtpSendingMessageHandler.class;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
protected AbstractFtpClientFactory<?> clientFactory() {
|
||||
return ClientFactorySupport.ftpClientFactory(this.host, this.port,
|
||||
this.remoteDirectory, this.username, this.password,
|
||||
this.clientMode, this.fileType);
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
@Override
|
||||
protected FtpSendingMessageHandler createInstance() throws Exception {
|
||||
AbstractFtpClientFactory<?> defaultFtpClientFactory = clientFactory();
|
||||
QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, defaultFtpClientFactory);
|
||||
FtpSendingMessageHandler ftpSendingMessageHandler = new FtpSendingMessageHandler(
|
||||
queuedFtpClientPool);
|
||||
ftpSendingMessageHandler.setFileNameGenerator(this.fileNameGenerator);
|
||||
if (this.charset != null) {
|
||||
ftpSendingMessageHandler.setCharset(this.charset);
|
||||
}
|
||||
ftpSendingMessageHandler.afterPropertiesSet();
|
||||
return ftpSendingMessageHandler;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
@@ -5,14 +21,13 @@ import org.apache.commons.net.ftp.FTPClient;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
|
||||
/**
|
||||
* Factory to make building the namespace easier
|
||||
* Factory to make building the namespace easier.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean
|
||||
extends FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean {
|
||||
public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean extends FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean {
|
||||
|
||||
/**
|
||||
* Sets whether the connection is implicit. Local testing reveals this to be a good choice.
|
||||
*/
|
||||
@@ -27,20 +42,30 @@ public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean
|
||||
* "P"
|
||||
*/
|
||||
protected volatile String prot;
|
||||
|
||||
private KeyManager keyManager;
|
||||
|
||||
private TrustManager trustManager;
|
||||
|
||||
protected volatile String authValue;
|
||||
|
||||
private Boolean sessionCreation;
|
||||
|
||||
private Boolean useClientMode;
|
||||
|
||||
private Boolean needClientAuth;
|
||||
|
||||
private Boolean wantsClientAuth;
|
||||
|
||||
private String[] cipherSuites;
|
||||
|
||||
|
||||
public FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean() {
|
||||
this.defaultFtpInboundFolderName = "ftpsInbound";
|
||||
this.clientMode = FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE;
|
||||
}
|
||||
|
||||
|
||||
public void setKeyManager(KeyManager keyManager) {
|
||||
this.keyManager = keyManager;
|
||||
}
|
||||
@@ -81,8 +106,11 @@ public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean
|
||||
this.wantsClientAuth = wantsClientAuth;
|
||||
}
|
||||
|
||||
protected AbstractFtpClientFactory defaultClientFactory()
|
||||
throws Exception {
|
||||
public void setCipherSuites(String[] cipherSuites) {
|
||||
this.cipherSuites = cipherSuites;
|
||||
}
|
||||
|
||||
protected AbstractFtpClientFactory<?> defaultClientFactory() throws Exception {
|
||||
DefaultFtpsClientFactory factory = ClientFactorySupport.ftpsClientFactory(this.host,
|
||||
Integer.parseInt(this.port), this.remoteDirectory,
|
||||
this.username, this.password, this.fileType, this.clientMode,
|
||||
@@ -94,7 +122,4 @@ public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean
|
||||
return factory;
|
||||
}
|
||||
|
||||
public void setCipherSuites(String[] cipherSuites) {
|
||||
this.cipherSuites = cipherSuites;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
/*
|
||||
* 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.ftp;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
|
||||
/**
|
||||
* Sends files to a remote FTPS file system. Based heavily on {@link org.springframework.integration.ftp.FtpSendingMessageHandler}
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
|
||||
public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandlerFactoryBean {
|
||||
|
||||
/**
|
||||
@@ -27,16 +41,26 @@ public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandl
|
||||
* "P"
|
||||
*/
|
||||
protected volatile String prot;
|
||||
|
||||
private KeyManager keyManager;
|
||||
|
||||
private TrustManager trustManager;
|
||||
|
||||
protected volatile String authValue;
|
||||
|
||||
private Boolean sessionCreation;
|
||||
|
||||
private Boolean useClientMode;
|
||||
|
||||
private Boolean needClientAuth;
|
||||
|
||||
private Boolean wantsClientAuth;
|
||||
|
||||
private String[] cipherSuites;
|
||||
|
||||
private int fileType;
|
||||
|
||||
|
||||
public void setImplicit(Boolean implicit) {
|
||||
this.implicit = implicit;
|
||||
}
|
||||
@@ -86,7 +110,7 @@ public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandl
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractFtpClientFactory clientFactory() {
|
||||
protected AbstractFtpClientFactory<?> clientFactory() {
|
||||
DefaultFtpsClientFactory factory = ClientFactorySupport.ftpsClientFactory(this.host,
|
||||
(this.port), this.remoteDirectory, this.username,
|
||||
this.password, this.fileType, this.clientMode, this.prot,
|
||||
@@ -94,7 +118,7 @@ public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandl
|
||||
this.trustManager, this.keyManager, this.sessionCreation,
|
||||
this.useClientMode, this.wantsClientAuth, this.needClientAuth,
|
||||
this.cipherSuites);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -25,7 +26,6 @@ import java.net.SocketException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
|
||||
|
||||
/**
|
||||
* FtpClientPool implementation based on a Queue. This implementation has a
|
||||
* default pool size of 5, but this is configurable with a constructor argument.
|
||||
@@ -36,22 +36,28 @@ import java.util.concurrent.ArrayBlockingQueue;
|
||||
* @author Iwein Fuld
|
||||
*/
|
||||
public class QueuedFtpClientPool implements FtpClientPool {
|
||||
private static final Log log = LogFactory.getLog(QueuedFtpClientPool.class);
|
||||
private static final int DEFAULT_POOL_SIZE = 5;
|
||||
private final Queue<FTPClient> pool;
|
||||
private final FtpClientFactory factory;
|
||||
|
||||
public QueuedFtpClientPool(FtpClientFactory factory) {
|
||||
private static final Log logger = LogFactory.getLog(QueuedFtpClientPool.class);
|
||||
|
||||
private static final int DEFAULT_POOL_SIZE = 5;
|
||||
|
||||
|
||||
private final Queue<FTPClient> pool;
|
||||
|
||||
private final FtpClientFactory<?> factory;
|
||||
|
||||
|
||||
public QueuedFtpClientPool(FtpClientFactory<?> factory) {
|
||||
this(DEFAULT_POOL_SIZE, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxPoolSize the maximum size of the pool
|
||||
*/
|
||||
public QueuedFtpClientPool(int maxPoolSize, FtpClientFactory factory) {
|
||||
Assert.notNull(factory);
|
||||
public QueuedFtpClientPool(int maxPoolSize, FtpClientFactory<?> factory) {
|
||||
Assert.notNull(factory, "factory must not be null");
|
||||
this.factory = factory;
|
||||
pool = new ArrayBlockingQueue<FTPClient>(maxPoolSize);
|
||||
this.pool = new ArrayBlockingQueue<FTPClient>(maxPoolSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,12 +71,10 @@ public class QueuedFtpClientPool implements FtpClientPool {
|
||||
* reason large pools are not recommended in poor networking conditions.
|
||||
*/
|
||||
public FTPClient getClient() throws SocketException, IOException {
|
||||
FTPClient client = pool.poll();
|
||||
|
||||
FTPClient client = this.pool.poll();
|
||||
if (client == null) {
|
||||
client = factory.getClient();
|
||||
client = this.factory.getClient();
|
||||
}
|
||||
|
||||
return prepareClient(client);
|
||||
}
|
||||
|
||||
@@ -88,8 +92,7 @@ public class QueuedFtpClientPool implements FtpClientPool {
|
||||
* @throws SocketException
|
||||
* @throws IOException
|
||||
*/
|
||||
protected FTPClient prepareClient(FTPClient client)
|
||||
throws SocketException, IOException {
|
||||
protected FTPClient prepareClient(FTPClient client) throws SocketException, IOException {
|
||||
return isClientAlive(client) ? client : getClient();
|
||||
}
|
||||
|
||||
@@ -98,20 +101,26 @@ public class QueuedFtpClientPool implements FtpClientPool {
|
||||
if (client.sendNoOp()) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Client [" + client + "] discarded: ", e);
|
||||
}
|
||||
|
||||
catch (IOException e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Client [" + client + "] discarded: ", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void releaseClient(FTPClient client) {
|
||||
if ((client != null) && !pool.offer(client)) {
|
||||
if ((client != null) && !this.pool.offer(client)) {
|
||||
try {
|
||||
client.disconnect();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error disconnecting ftpclient", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Error disconnecting ftpclient", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ftp.config;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
@@ -8,25 +24,21 @@ import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.ftp.FtpSendingMessageHandlerFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
/**
|
||||
* Logic for parsing the ftp:outbound-channel-adapter
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpMessageSendingConsumerBeanDefinitionParser
|
||||
extends AbstractOutboundChannelAdapterParser {
|
||||
public class FtpMessageSendingConsumerBeanDefinitionParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element,
|
||||
ParserContext parserContext) {
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
FtpSendingMessageHandlerFactoryBean.class.getName());
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder,element,"charset");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder,element,"filename-generator", "fileNameGenerator");
|
||||
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element,
|
||||
parserContext);
|
||||
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext);
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +1,58 @@
|
||||
/*
|
||||
* 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.ftp.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.ftp.FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Logic that configures an ftp:inbound-channel-adapter
|
||||
* Parser for the FTP inbound-channel-adapter.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpMessageSourceBeanDefinitionParser
|
||||
extends AbstractPollingInboundChannelAdapterParser {
|
||||
public class FtpMessageSourceBeanDefinitionParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
private Set<String> receiveAttrs = new HashSet<String>(Arrays.asList(
|
||||
"auto-delete-remote-files-on-sync,filename-pattern,local-working-directory".split(
|
||||
",")));
|
||||
"auto-delete-remote-files-on-sync,filename-pattern,local-working-directory".split(",")));
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder,
|
||||
element, "filter");
|
||||
|
||||
for (String a : receiveAttrs)
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder,
|
||||
element, a);
|
||||
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element,
|
||||
parserContext);
|
||||
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(),
|
||||
parserContext.getRegistry());
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "filter");
|
||||
for (String a : receiveAttrs) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, a);
|
||||
}
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext);
|
||||
String beanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return new RuntimeBeanReference(beanName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 the original author or authors
|
||||
* 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.
|
||||
@@ -13,15 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.ftp.config;
|
||||
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Provides namespace support for using FTP
|
||||
* <p/>
|
||||
@@ -29,9 +29,10 @@ import java.util.Map;
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class FtpNamespaceHandler extends NamespaceHandlerSupport {
|
||||
public class FtpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
static public Map<String, Integer> FILE_TYPES = new HashMap<String, Integer>();
|
||||
|
||||
static public Map<String, Integer> CLIENT_MODES = new HashMap<String, Integer>();
|
||||
|
||||
static {
|
||||
@@ -47,10 +48,10 @@ public class FtpNamespaceHandler extends NamespaceHandlerSupport {
|
||||
CLIENT_MODES.put("passive-remote-data-connection-mode", 3);
|
||||
}
|
||||
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("inbound-channel-adapter",
|
||||
new FtpMessageSourceBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("outbound-channel-adapter",
|
||||
new FtpMessageSendingConsumerBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new FtpMessageSourceBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new FtpMessageSendingConsumerBeanDefinitionParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,42 +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.ftp.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
/**
|
||||
* A lot of parsers need to support the same set of core attributes, so I'm hiding that logic here
|
||||
* General support for parsers in the FTP namespace.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpNamespaceParserSupport {
|
||||
|
||||
/**
|
||||
* lots of values are supported across all adapters, let this code handle it initially
|
||||
*
|
||||
* Handles values that are supported across all adapters.
|
||||
* @param builder a builder
|
||||
* @param element an element
|
||||
* @param parserContext a parser context
|
||||
*/
|
||||
public static void configureCoreFtpClient(BeanDefinitionBuilder builder,
|
||||
Element element, ParserContext parserContext) {
|
||||
for (String p : "auto-create-directories,username,port,password,host,remote-directory".split(
|
||||
",")) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder,
|
||||
element, p);
|
||||
public static void configureCoreFtpClient(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
|
||||
for (String p : "auto-create-directories,username,port,password,host,remote-directory".split(",")) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, p);
|
||||
}
|
||||
|
||||
if (element.hasAttribute("file-type")) {
|
||||
int fileType = FtpNamespaceHandler.FILE_TYPES.get(element.getAttribute(
|
||||
"file-type"));
|
||||
int fileType = FtpNamespaceHandler.FILE_TYPES.get(element.getAttribute("file-type"));
|
||||
builder.addPropertyValue("fileType", fileType);
|
||||
}
|
||||
|
||||
if (element.hasAttribute("client-mode")) {
|
||||
int clientMode = FtpNamespaceHandler.CLIENT_MODES.get(element.getAttribute(
|
||||
"client-mode"));
|
||||
int clientMode = FtpNamespaceHandler.CLIENT_MODES.get(element.getAttribute("client-mode"));
|
||||
builder.addPropertyValue("clientMode", clientMode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +1,44 @@
|
||||
/*
|
||||
* 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.ftp.config;
|
||||
|
||||
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.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.ftp.FtpsSendingMessageHandlerFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
/**
|
||||
* Logic for parsing the ftp:outbound-channel-adapter
|
||||
* Parser for the FTPS outbound-channel-adapter
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpsMessageSendingConsumerBeanDefinitionParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element,
|
||||
ParserContext parserContext) {
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
FtpsSendingMessageHandlerFactoryBean.class.getName());
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder,element,"charset");
|
||||
|
||||
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element,
|
||||
parserContext);
|
||||
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext);
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +1,54 @@
|
||||
package org.springframework.integration.ftp.config;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.ftp.FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
package org.springframework.integration.ftp.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.ftp.FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean;
|
||||
|
||||
/**
|
||||
* Logic that configures an ftp:inbound-channel-adapter
|
||||
* Parser for the FTPS inbound-channel-adapter
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpsMessageSourceBeanDefinitionParser
|
||||
extends AbstractPollingInboundChannelAdapterParser {
|
||||
public class FtpsMessageSourceBeanDefinitionParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
private Set<String> receiveAttrs = new HashSet<String>(Arrays.asList(
|
||||
"auto-delete-remote-files-on-sync,filename-pattern,local-working-directory".split(
|
||||
",")));
|
||||
"auto-delete-remote-files-on-sync,filename-pattern,local-working-directory".split(",")));
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder,
|
||||
element, "filter");
|
||||
|
||||
for (String a : receiveAttrs)
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder,
|
||||
element, a);
|
||||
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element,
|
||||
parserContext);
|
||||
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(),
|
||||
parserContext.getRegistry());
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "filter");
|
||||
for (String a : receiveAttrs) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, a);
|
||||
}
|
||||
FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext);
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 the original author or authors
|
||||
* 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.
|
||||
@@ -13,25 +13,22 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.ftp.config;
|
||||
|
||||
|
||||
/**
|
||||
* Provides namespace support for using FTP
|
||||
* Provides namespace support for using FTP.
|
||||
* <p/>
|
||||
* This is *heavily* influenced by the good work done by Iwein before.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class FtpsNamespaceHandler extends FtpNamespaceHandler {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.registerBeanDefinitionParser("inbound-channel-adapter",
|
||||
new FtpsMessageSourceBeanDefinitionParser());
|
||||
|
||||
// todo test this
|
||||
this.registerBeanDefinitionParser("outbound-channel-adapter",
|
||||
new FtpsMessageSendingConsumerBeanDefinitionParser());
|
||||
this.registerBeanDefinitionParser("inbound-channel-adapter", new FtpsMessageSourceBeanDefinitionParser());
|
||||
this.registerBeanDefinitionParser("outbound-channel-adapter", new FtpsMessageSendingConsumerBeanDefinitionParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
package org.springframework.integration.ftp.impl;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
import org.springframework.integration.file.entries.CompositeEntryListFilter;
|
||||
import org.springframework.integration.file.entries.EntryListFilter;
|
||||
import org.springframework.integration.file.entries.PatternMatchingEntryListFilter;
|
||||
import org.springframework.integration.ftp.*;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
|
||||
/**
|
||||
* Factory to make building the namespace easier
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean extends FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean {
|
||||
/**
|
||||
* Sets whether the connection is implicit. Local testing reveals this to be a good choice.
|
||||
*/
|
||||
protected volatile Boolean implicit = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* "TLS" or "SSL"
|
||||
*/
|
||||
protected volatile String protocol;
|
||||
|
||||
/**
|
||||
* "P"
|
||||
*/
|
||||
protected volatile String prot;
|
||||
private KeyManager keyManager;
|
||||
private TrustManager trustManager;
|
||||
protected volatile String authValue;
|
||||
private Boolean sessionCreation;
|
||||
private Boolean useClientMode;
|
||||
private Boolean needClientAuth;
|
||||
private Boolean wantsClientAuth;
|
||||
private String[] cipherSuites;
|
||||
|
||||
public FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean() {
|
||||
this.defaultFtpInboundFolderName = "ftpsInbound";
|
||||
this.clientMode = FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE;
|
||||
}
|
||||
|
||||
public void setKeyManager(KeyManager keyManager) {
|
||||
this.keyManager = keyManager;
|
||||
}
|
||||
|
||||
public void setTrustManager(TrustManager trustManager) {
|
||||
this.trustManager = trustManager;
|
||||
}
|
||||
|
||||
public void setImplicit(Boolean implicit) {
|
||||
this.implicit = implicit;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public void setProt(String prot) {
|
||||
this.prot = prot;
|
||||
}
|
||||
|
||||
public void setAuthValue(String authValue) {
|
||||
this.authValue = authValue;
|
||||
}
|
||||
|
||||
public void setSessionCreation(Boolean sessionCreation) {
|
||||
this.sessionCreation = sessionCreation;
|
||||
}
|
||||
|
||||
public void setUseClientMode(Boolean useClientMode) {
|
||||
this.useClientMode = useClientMode;
|
||||
}
|
||||
|
||||
public void setNeedClientAuth(Boolean needClientAuth) {
|
||||
this.needClientAuth = needClientAuth;
|
||||
}
|
||||
|
||||
public void setWantsClientAuth(Boolean wantsClientAuth) {
|
||||
this.wantsClientAuth = wantsClientAuth;
|
||||
}
|
||||
|
||||
protected AbstractFtpClientFactory defaultClientFactory()
|
||||
throws Exception {
|
||||
DefaultFtpsClientFactory factory = ClientFactorySupport.ftpsClientFactory(this.host, Integer.parseInt(this.port), this.remoteDirectory, this.username, this.password, this.fileType,
|
||||
this.clientMode, this.prot, this.protocol, this.authValue, this.implicit, this.trustManager, this.keyManager, this.sessionCreation, this.useClientMode, this.wantsClientAuth,
|
||||
this.needClientAuth, this.cipherSuites);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
public void setCipherSuites(String[] cipherSuites) {
|
||||
this.cipherSuites = cipherSuites;
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,10 @@
|
||||
<xsd:import namespace="http://www.springframework.org/schema/integration"
|
||||
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd"/>
|
||||
|
||||
|
||||
<xsd:element name="outbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Builds an outbound-channel-adapter that writes files to a remote FTP endpoint.
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
@@ -49,7 +46,6 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
<xsd:attribute name="username" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="remote-directory" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="host" type="xsd:string" use="required"/>
|
||||
@@ -59,7 +55,7 @@
|
||||
<xsd:attribute name="filename-generator" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Allows you to specify a reference to
|
||||
Allows you to specify a reference to
|
||||
[org.springframework.integration.file.FileNameGenerator] implementation.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
@@ -71,10 +67,9 @@
|
||||
</xsd:attribute>
|
||||
<xsd:attribute use="optional" name="client-mode" default="active-local-data-connection-mode">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
the FTP Client-Mode.
|
||||
|
||||
/***
|
||||
<xsd:documentation><![CDATA[the FTP Client-Mode
|
||||
|
||||
/***
|
||||
* A constant indicating the FTP session is expecting all transfers
|
||||
* to occur between the client (local) and server and that the server
|
||||
* should connect to the client's data port to initiate a data transfer.
|
||||
@@ -107,8 +102,6 @@
|
||||
* transfer.
|
||||
***/
|
||||
PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3
|
||||
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
@@ -120,13 +113,10 @@
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
|
||||
<xsd:attribute use="optional" name="file-type" default="binary-file-type">
|
||||
<xsd:attribute name="file-type" default="binary-file-type" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Binary, ASCII, or EBDIC. Binary's a good default
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
@@ -137,17 +127,13 @@
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
|
||||
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Builds an inbound-channel-adapter that synchronizes a local directory with the contents of a remote FTP endpoint.
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
@@ -164,12 +150,10 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute use="optional" name="file-type" default="binary-file-type">
|
||||
<xsd:attribute name="file-type" default="binary-file-type" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Binary, ASCII, or EBDIC. Binary's a good default
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
@@ -190,24 +174,19 @@
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="filename-pattern" type="xsd:string"/>
|
||||
|
||||
|
||||
<xsd:attribute name="local-working-directory" type="xsd:string"/>
|
||||
<xsd:attribute name="auto-create-directories" type="xsd:boolean"/>
|
||||
<xsd:attribute name="auto-delete-remote-files-on-sync" type="xsd:boolean"/>
|
||||
|
||||
|
||||
<xsd:attribute name="username" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="remote-directory" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="host" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="password" type="xsd:string"/>
|
||||
<xsd:attribute name="port" type="xsd:int" default="22"/>
|
||||
<xsd:attribute use="optional" name="client-mode" default="active-local-data-connection-mode">
|
||||
<xsd:attribute name="client-mode" default="active-local-data-connection-mode" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
the FTP Client-Mode.
|
||||
|
||||
/***
|
||||
<xsd:documentation><![CDATA[the FTP Client-Mode
|
||||
|
||||
/***
|
||||
* A constant indicating the FTP session is expecting all transfers
|
||||
* to occur between the client (local) and server and that the server
|
||||
* should connect to the client's data port to initiate a data transfer.
|
||||
@@ -240,8 +219,6 @@
|
||||
* transfer.
|
||||
***/
|
||||
PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3
|
||||
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
@@ -250,13 +227,10 @@
|
||||
<xsd:enumeration value="active-remote-data-connection-mode"/>
|
||||
<xsd:enumeration value="passive-local-data-connection-mode"/>
|
||||
<xsd:enumeration value="passive-remote-data-connection-mode"/>
|
||||
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
</xsd:schema>
|
||||
|
||||
@@ -29,13 +29,10 @@
|
||||
<xsd:import namespace="http://www.springframework.org/schema/integration"
|
||||
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd"/>
|
||||
|
||||
|
||||
<xsd:element name="outbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Builds an outbound-channel-adapter that writes files to a remote FTPS endpoint.
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
@@ -49,8 +46,6 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
|
||||
<xsd:attribute name="username" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="remote-directory" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="host" type="xsd:string" use="required"/>
|
||||
@@ -70,10 +65,10 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute use="optional" name="client-mode" default="active-local-data-connection-mode">
|
||||
<xsd:attribute name="client-mode" default="active-local-data-connection-mode" use="optional" >
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
the FTP Client-Mode.
|
||||
the FTP Client-Mode
|
||||
|
||||
/***
|
||||
* A constant indicating the FTP session is expecting all transfers
|
||||
@@ -109,7 +104,6 @@
|
||||
***/
|
||||
PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3
|
||||
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
@@ -121,14 +115,11 @@
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
|
||||
<xsd:attribute use="optional" name="file-type" default="binary-file-type">
|
||||
<xsd:attribute name="file-type" default="binary-file-type" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Binary, ASCII, or EBDIC. Binary's a good default
|
||||
|
||||
]]></xsd:documentation>
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:NMTOKEN">
|
||||
@@ -138,26 +129,19 @@
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
|
||||
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="inbound-channel-adapter">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Builds an inbound-channel-adapter that synchronizes a local directory with the contents of a remote FTP endpoint.
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
|
||||
|
||||
|
||||
<xsd:attribute name="id" type="xsd:string"/>
|
||||
<xsd:attribute name="channel" use="required" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
@@ -171,10 +155,8 @@
|
||||
<xsd:attribute use="optional" name="file-type" default="binary-file-type">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
Binary, ASCII, or EBDIC. Binary's a good default
|
||||
|
||||
]]></xsd:documentation>
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:NMTOKEN">
|
||||
@@ -194,13 +176,9 @@
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="filename-pattern" type="xsd:string"/>
|
||||
|
||||
|
||||
<xsd:attribute name="local-working-directory" type="xsd:string"/>
|
||||
<xsd:attribute name="auto-create-directories" type="xsd:boolean"/>
|
||||
<xsd:attribute name="auto-delete-remote-files-on-sync" type="xsd:boolean"/>
|
||||
|
||||
|
||||
<xsd:attribute name="username" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="remote-directory" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="host" type="xsd:string" use="required"/>
|
||||
@@ -244,8 +222,6 @@
|
||||
* transfer.
|
||||
***/
|
||||
PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3
|
||||
|
||||
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
@@ -254,7 +230,6 @@
|
||||
<xsd:enumeration value="active-remote-data-connection-mode"/>
|
||||
<xsd:enumeration value="passive-local-data-connection-mode"/>
|
||||
<xsd:enumeration value="passive-remote-data-connection-mode"/>
|
||||
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
@@ -262,5 +237,4 @@
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
</xsd:schema>
|
||||
|
||||
@@ -4,8 +4,5 @@ log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
|
||||
|
||||
|
||||
log4j.category.org.springframework=WARN
|
||||
# log4j.category.org.springframework.integration=DEBUG
|
||||
# log4j.category.org.springframework.integration.jdbc=DEBUG
|
||||
log4j.category.org.springframework.ftp=DEBUG
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
@@ -47,7 +48,6 @@ import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.mapping.HeaderMapper;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.util.SimpleBeanResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
@@ -202,7 +202,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
super.onInit();
|
||||
BeanFactory beanFactory = this.getBeanFactory();
|
||||
if (beanFactory != null) {
|
||||
this.evaluationContext.setBeanResolver(new SimpleBeanResolver(beanFactory));
|
||||
this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
||||
}
|
||||
ConversionService conversionService = this.getConversionService();
|
||||
if (conversionService != null) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.ip.util;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.integration.ip.util.RegexUtils.escapeRegExSpecials;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
|
||||
@@ -41,6 +42,7 @@ public class RegexUtilsTests {
|
||||
* And one of the ones we are actually using
|
||||
*/
|
||||
@Test
|
||||
@Ignore // no longer relevant due to INT-1568
|
||||
public void testSiPrefix () {
|
||||
// protect the test in case we ever change the prefix
|
||||
if ("$^[]{()}+*\\?|.".contains(MessageHeaders.PREFIX)) {
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
package org.springframework.integration.jdbc.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for {@link org.springframework.integration.jdbc.JdbcPollingChannelAdapter}.
|
||||
@@ -42,7 +43,7 @@ public class JdbcPollingChannelAdapterParser extends AbstractPollingInboundChann
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition("org.springframework.integration.jdbc.JdbcPollingChannelAdapter");
|
||||
@@ -75,8 +76,7 @@ public class JdbcPollingChannelAdapterParser extends AbstractPollingInboundChann
|
||||
builder.addPropertyValue("updateSql", update);
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "update-per-row");
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class JdbcMessageHandlerIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testIdHeaderDynamicInsert() {
|
||||
JdbcMessageHandler handler = new JdbcMessageHandler(jdbcTemplate.getJdbcOperations(), "insert into foos (id, status, name) values (:headers[$id], 0, :payload)");
|
||||
JdbcMessageHandler handler = new JdbcMessageHandler(jdbcTemplate.getJdbcOperations(), "insert into foos (id, status, name) values (:headers[id], 0, :payload)");
|
||||
Message<String> message = new GenericMessage<String>("foo");
|
||||
handler.handleMessage(message);
|
||||
String id = message.getHeaders().getId().toString();
|
||||
|
||||
@@ -37,10 +37,10 @@ public class JdbcMessageHandlerParserTests {
|
||||
@Test
|
||||
public void testDollarHeaderOutboundChannelAdapter(){
|
||||
setUp("handlingDollarHeaderJdbcOutboundChannelAdapterTest.xml", getClass());
|
||||
Message<?> message = MessageBuilder.withPayload("foo").build();
|
||||
Message<?> message = MessageBuilder.withPayload("foo").setHeader("$foo_id", "abc").build();
|
||||
channel.send(message);
|
||||
Map<String, Object> map = this.jdbcTemplate.queryForMap("SELECT * from FOOS");
|
||||
assertEquals("Wrong id", message.getHeaders().getId().toString(), map.get("ID"));
|
||||
assertEquals("Wrong id", message.getHeaders().get("$foo_id").toString(), map.get("ID"));
|
||||
assertEquals("Wrong id", "foo", map.get("name"));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
http://www.springframework.org/schema/integration/jdbc
|
||||
http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd">
|
||||
|
||||
<outbound-channel-adapter query="insert into foos (id, status, name) values (:headers[$id], 0, :payload)"
|
||||
<outbound-channel-adapter query="insert into foos (id, status, name) values (:headers[$foo_id], 0, :payload)"
|
||||
channel="target" data-source="dataSource"/>
|
||||
|
||||
<beans:import resource="jdbcOutboundChannelAdapterCommonConfig.xml" />
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
http://www.springframework.org/schema/integration/jdbc
|
||||
http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd">
|
||||
|
||||
<outbound-channel-adapter query="insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])"
|
||||
<outbound-channel-adapter query="insert into foos (id, status, name) values (:headers[id], 0, :payload[foo])"
|
||||
channel="target" data-source="dataSource"/>
|
||||
|
||||
<beans:import resource="jdbcOutboundChannelAdapterCommonConfig.xml" />
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<si:queue />
|
||||
</si:channel>
|
||||
|
||||
<outbound-gateway query="select * from foos where id=:headers[$id]" update="insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])"
|
||||
<outbound-gateway query="select * from foos where id=:headers[id]" update="insert into foos (id, status, name) values (:headers[id], 0, :payload[foo])"
|
||||
request-channel="target" reply-channel="output" data-source="dataSource" />
|
||||
|
||||
<beans:import resource="jdbcOutboundChannelAdapterCommonConfig.xml" />
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd">
|
||||
|
||||
<outbound-channel-adapter channel="target" data-source="dataSource">
|
||||
<query>insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])</query>
|
||||
<query>insert into foos (id, status, name) values (:headers[id], 0, :payload[foo])</query>
|
||||
</outbound-channel-adapter>
|
||||
|
||||
<beans:import resource="jdbcOutboundChannelAdapterCommonConfig.xml" />
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
http://www.springframework.org/schema/integration/jdbc
|
||||
http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd">
|
||||
|
||||
<outbound-channel-adapter query="insert into foos (id, status, name) values (:headers[$id], 0, :foo)"
|
||||
<outbound-channel-adapter query="insert into foos (id, status, name) values (:headers[id], 0, :foo)"
|
||||
channel="target" data-source="dataSource" sql-parameter-source-factory="sqlParameterSourceFactory"/>
|
||||
|
||||
<beans:import resource="jdbcOutboundChannelAdapterCommonConfig.xml" />
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.integration.jms.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
@@ -43,7 +44,7 @@ public class JmsInboundChannelAdapterParser extends AbstractPollingInboundChanne
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.jms.JmsDestinationPollingSource");
|
||||
String componentName = this.resolveId(element, builder.getBeanDefinition(), parserContext);
|
||||
@@ -88,9 +89,7 @@ public class JmsInboundChannelAdapterParser extends AbstractPollingInboundChanne
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "selector", "messageSelector");
|
||||
BeanDefinition beanDefinition = builder.getBeanDefinition();
|
||||
String beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, parserContext.getRegistry());
|
||||
BeanComponentDefinition component = new BeanComponentDefinition(beanDefinition, beanName);
|
||||
parserContext.registerBeanComponent(component);
|
||||
return beanName;
|
||||
return new BeanComponentDefinition(beanDefinition, beanName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.springframework.integration.jmx.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
@@ -36,14 +36,13 @@ public class AttributePollingChannelAdapterParser extends AbstractPollingInbound
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.integration.jmx.AttributePollingMessageSource");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "server", "server");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "object-name");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "attribute-name");
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ package org.springframework.integration.mail.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
@@ -28,8 +28,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <inbound-channel-adapter> element of Spring
|
||||
* Integration's 'mail' namespace.
|
||||
* Parser for the <inbound-channel-adapter> element of Spring Integration's 'mail' namespace.
|
||||
*
|
||||
* @author Jonas Partner
|
||||
* @author Mark Fisher
|
||||
@@ -41,12 +40,11 @@ public class MailInboundChannelAdapterParser extends AbstractPollingInboundChann
|
||||
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
BASE_PACKAGE + ".MailReceivingMessageSource");
|
||||
builder.addConstructorArgValue(this.parseMailReceiver(element, parserContext));
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanDefinition parseMailReceiver(Element element, ParserContext parserContext) {
|
||||
@@ -85,7 +83,6 @@ public class MailInboundChannelAdapterParser extends AbstractPollingInboundChann
|
||||
if (StringUtils.hasText(markAsRead)){
|
||||
receiverBuilder.addPropertyValue("shouldMarkMessagesAsRead", markAsRead);
|
||||
}
|
||||
|
||||
return receiverBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,27 +13,34 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This approach - of having a SessionPool ({@link SftpSessionPool}) that has an
|
||||
* implementation of Queued*SessionPool ({@link QueuedSftpSessionPool}) - was
|
||||
* taken pretty directly from the incredibly good Spring IntegrationFTP adapter.
|
||||
* taken almost directly from the Spring Integration FTP adapter.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.0
|
||||
*/
|
||||
public class QueuedSftpSessionPool implements SftpSessionPool, InitializingBean {
|
||||
|
||||
public static final int DEFAULT_POOL_SIZE = 10;
|
||||
private Queue<SftpSession> queue;
|
||||
|
||||
|
||||
private volatile Queue<SftpSession> queue;
|
||||
|
||||
private final SftpSessionFactory sftpSessionFactory;
|
||||
private int maxPoolSize;
|
||||
|
||||
private final int maxPoolSize;
|
||||
|
||||
|
||||
public QueuedSftpSessionPool(SftpSessionFactory factory) {
|
||||
this(DEFAULT_POOL_SIZE, factory);
|
||||
@@ -44,34 +51,32 @@ public class QueuedSftpSessionPool implements SftpSessionPool, InitializingBean
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
assert maxPoolSize > 0 : "poolSize must be greater than 0!";
|
||||
queue = new ArrayBlockingQueue<SftpSession>(maxPoolSize, true); // size, faireness to avoid starvation
|
||||
assert sftpSessionFactory != null : "sftpSessionFactory must not be null!";
|
||||
Assert.notNull(this.sftpSessionFactory, "sftpSessionFactory must not be null");
|
||||
Assert.isTrue(this.maxPoolSize > 0, "poolSize must be greater than 0");
|
||||
this.queue = new ArrayBlockingQueue<SftpSession>(this.maxPoolSize, true); // size, fairness to avoid starvation
|
||||
}
|
||||
|
||||
public SftpSession getSession() throws Exception {
|
||||
SftpSession session = this.queue.poll();
|
||||
|
||||
if (null == session) {
|
||||
session = this.sftpSessionFactory.getObject();
|
||||
|
||||
if (queue.size() < maxPoolSize) {
|
||||
queue.add(session);
|
||||
if (this.queue.size() < this.maxPoolSize) {
|
||||
this.queue.add(session);
|
||||
}
|
||||
}
|
||||
|
||||
if (null == session) {
|
||||
session = queue.poll();
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
public void release(SftpSession session) {
|
||||
if (queue.size() < maxPoolSize) {
|
||||
queue.add(session); // somehow one snuck in before <code>session</code> was finished!
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
dispose(session);
|
||||
}
|
||||
}
|
||||
@@ -80,18 +85,16 @@ public class QueuedSftpSessionPool implements SftpSessionPool, InitializingBean
|
||||
if (s == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (queue.contains(s)) //this should never happen, but if it does ...
|
||||
{
|
||||
if (queue.contains(s)) {
|
||||
//this should never happen, but if it does...
|
||||
queue.remove(s);
|
||||
}
|
||||
|
||||
if ((s.getChannel() != null) && s.getChannel().isConnected()) {
|
||||
s.getChannel().disconnect();
|
||||
}
|
||||
|
||||
if (s.getSession().isConnected()) {
|
||||
s.getSession().disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,13 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import org.springframework.integration.file.entries.EntryNamer;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
|
||||
/**
|
||||
* Knows how to name a {@link com.jcraft.jsch.ChannelSftp.LsEntry} instance
|
||||
* Stratgy for naming a {@link com.jcraft.jsch.ChannelSftp.LsEntry} instance.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
@@ -28,4 +30,5 @@ public class SftpEntryNamer implements EntryNamer<ChannelSftp.LsEntry> {
|
||||
public String nameOf(ChannelSftp.LsEntry entry) {
|
||||
return entry.getFilename();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
/**
|
||||
* @author Josh Long
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SftpConstants {
|
||||
public static final String SFTP_REMOTE_DIRECTORY_HEADER = "SFTP_REMOTE_DIRECTORY_HEADER";
|
||||
public abstract class SftpHeaders {
|
||||
|
||||
public static final String REMOTE_DIRECTORY = "sftp_remoteDirectory";
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageDeliveryException;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
@@ -43,9 +44,8 @@ import org.springframework.util.FileCopyUtils;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
|
||||
/**
|
||||
* Sending a message payload to a remote SFTP endpoint. For now, we assume that the payload of the inbound message is of
|
||||
* type {@link java.io.File}. Perhaps we could support a payload of java.io.InputStream with a Header designating the file
|
||||
* name?
|
||||
* Sends message payloads to a remote SFTP endpoint.
|
||||
* Assumes that the payload of the inbound message is of type {@link java.io.File}.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.0
|
||||
@@ -65,7 +65,7 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
|
||||
private volatile Resource temporaryBufferFolder = new FileSystemResource(SystemUtils.getJavaIoTmpDir());
|
||||
|
||||
private volatile boolean afterPropertiesSetRan;
|
||||
private volatile boolean initialized;
|
||||
|
||||
private volatile String charset = Charset.defaultCharset().name();
|
||||
|
||||
@@ -88,7 +88,7 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
}
|
||||
|
||||
public String getRemoteDirectory() {
|
||||
return remoteDirectory;
|
||||
return this.remoteDirectory;
|
||||
}
|
||||
|
||||
public void setCharset(String charset) {
|
||||
@@ -96,19 +96,16 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.state(this.pool != null, "the pool can't be null!");
|
||||
temporaryBufferFolderFile = this.temporaryBufferFolder.getFile();
|
||||
if (!afterPropertiesSetRan) {
|
||||
Assert.notNull(this.pool, "the pool must not be null");
|
||||
this.temporaryBufferFolderFile = this.temporaryBufferFolder.getFile();
|
||||
if (!this.initialized) {
|
||||
if (StringUtils.isEmpty(this.remoteDirectory)) {
|
||||
remoteDirectory = null;
|
||||
this.remoteDirectory = null;
|
||||
}
|
||||
this.afterPropertiesSetRan = true;
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Ugh this needs to be put in a convenient place accessible for all the file:, sftp:, and ftp:* adapters */
|
||||
|
||||
private File handleFileMessage(File sourceFile, File tempFile, File resultFile) throws IOException {
|
||||
if (sourceFile.renameTo(resultFile)) {
|
||||
return resultFile;
|
||||
@@ -131,13 +128,13 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
return resultFile;
|
||||
}
|
||||
|
||||
private File redeemForStorableFile(Message<?> msg) throws MessageDeliveryException {
|
||||
private File redeemForStorableFile(Message<?> message) throws MessageDeliveryException {
|
||||
try {
|
||||
Object payload = msg.getPayload();
|
||||
String generateFileName = this.fileNameGenerator.generateFileName(msg);
|
||||
File tempFile = new File(temporaryBufferFolderFile, generateFileName + TEMPORARY_FILE_SUFFIX);
|
||||
File resultFile = new File(temporaryBufferFolderFile, generateFileName);
|
||||
File sendableFile;
|
||||
Object payload = message.getPayload();
|
||||
String generateFileName = this.fileNameGenerator.generateFileName(message);
|
||||
File tempFile = new File(this.temporaryBufferFolderFile, generateFileName + TEMPORARY_FILE_SUFFIX);
|
||||
File resultFile = new File(this.temporaryBufferFolderFile, generateFileName);
|
||||
File sendableFile = null;
|
||||
if (payload instanceof String) {
|
||||
sendableFile = this.handleStringMessage((String) payload, tempFile, resultFile, this.charset);
|
||||
}
|
||||
@@ -147,30 +144,23 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
else if (payload instanceof byte[]) {
|
||||
sendableFile = this.handleByteArrayMessage((byte[]) payload, tempFile, resultFile);
|
||||
}
|
||||
else {
|
||||
sendableFile = null;
|
||||
}
|
||||
return sendableFile;
|
||||
}
|
||||
catch (Throwable th) {
|
||||
throw new MessageDeliveryException(msg);
|
||||
throw new MessageDeliveryException(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Ugh this needs to be put in a convenient place accessible for all the file:, sftp:, and ftp:* adapters */
|
||||
|
||||
public void handleMessage(final Message<?> message) {
|
||||
Assert.state(this.pool != null, "need a working pool");
|
||||
Assert.notNull(this.pool, "the pool must not be null");
|
||||
File inboundFilePayload = this.redeemForStorableFile(message);
|
||||
try {
|
||||
if ((inboundFilePayload != null) && inboundFilePayload.exists()) {
|
||||
sendFileToRemoteEndpoint(message, inboundFilePayload);
|
||||
}
|
||||
}
|
||||
catch (Throwable thr) {
|
||||
// logger.debug("recieved an exception.", thr);
|
||||
throw new MessageDeliveryException(message, "couldn't deliver the message!", thr);
|
||||
catch (Exception e) {
|
||||
throw new MessageDeliveryException(message, "failed to deliver the message", e);
|
||||
}
|
||||
finally {
|
||||
if (inboundFilePayload != null && inboundFilePayload.exists())
|
||||
@@ -178,11 +168,11 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendFileToRemoteEndpoint(Message<?> message, File file) throws Throwable {
|
||||
assert this.pool != null : "need a working pool";
|
||||
private boolean sendFileToRemoteEndpoint(Message<?> message, File file) throws Exception {
|
||||
Assert.notNull(this.pool, "pool must not be null");
|
||||
SftpSession session = this.pool.getSession();
|
||||
if (session == null) {
|
||||
throw new RuntimeException("the session returned from the pool is null, can't possibly proceed.");
|
||||
throw new MessagingException("The session returned from the pool is null, cannot proceed.");
|
||||
}
|
||||
session.start();
|
||||
ChannelSftp sftp = session.getChannel();
|
||||
@@ -190,13 +180,12 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
try {
|
||||
fileInputStream = new FileInputStream(file);
|
||||
String baseOfRemotePath = StringUtils.isEmpty(this.remoteDirectory) ? StringUtils.EMPTY : remoteDirectory; // the safe default
|
||||
// logger.debug("going to send " + file.getAbsolutePath() + " to a remote sftp endpoint");
|
||||
String dynRd = null;
|
||||
MessageHeaders messageHeaders = null;
|
||||
if (message != null) {
|
||||
messageHeaders = message.getHeaders();
|
||||
if ((messageHeaders != null) && messageHeaders.containsKey(SftpConstants.SFTP_REMOTE_DIRECTORY_HEADER)) {
|
||||
dynRd = (String) messageHeaders.get(SftpConstants.SFTP_REMOTE_DIRECTORY_HEADER);
|
||||
if ((messageHeaders != null) && messageHeaders.containsKey(SftpHeaders.REMOTE_DIRECTORY)) {
|
||||
dynRd = (String) messageHeaders.get(SftpHeaders.REMOTE_DIRECTORY);
|
||||
if (!StringUtils.isEmpty(dynRd)) {
|
||||
baseOfRemotePath = dynRd;
|
||||
}
|
||||
@@ -210,8 +199,8 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(fileInputStream);
|
||||
if (pool != null) {
|
||||
pool.release(session);
|
||||
if (this.pool != null) {
|
||||
this.pool.release(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
@@ -23,9 +24,7 @@ import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
/**
|
||||
* c
|
||||
* There are many ways to create a {@link SftpSession} just as there are many ways to SSH into a remote system.
|
||||
* You may use a username and password, you may use a username and private key, you may use a username and a private key with a password, etc.
|
||||
* <p/>
|
||||
@@ -36,10 +35,15 @@ import java.io.InputStream;
|
||||
* @author Mario Gray
|
||||
*/
|
||||
public class SftpSession {
|
||||
|
||||
private volatile ChannelSftp channel;
|
||||
|
||||
private volatile Session session;
|
||||
|
||||
private String privateKey;
|
||||
|
||||
private String privateKeyPassphrase;
|
||||
|
||||
private volatile UserInfo userInfo;
|
||||
|
||||
|
||||
@@ -66,44 +70,41 @@ public class SftpSession {
|
||||
* to surmount that, we need the private key passphrase. Specify that here.
|
||||
* @throws Exception thrown if any of a myriad of scenarios plays out
|
||||
*/
|
||||
public SftpSession(String userName, String hostName, String userPassword, int port, String knownHostsFile, InputStream knownHostsInputStream, String privateKey, String pvKeyPassPhrase)
|
||||
throws Exception {
|
||||
JSch jSch = new JSch();
|
||||
public SftpSession(String userName, String hostName, String userPassword, int port, String knownHostsFile,
|
||||
InputStream knownHostsInputStream, String privateKey, String pvKeyPassPhrase) throws Exception {
|
||||
|
||||
JSch jSch = new JSch();
|
||||
if (port <= 0) {
|
||||
port = 22;
|
||||
}
|
||||
|
||||
this.privateKey = privateKey;
|
||||
this.privateKeyPassphrase = pvKeyPassPhrase;
|
||||
|
||||
if (!StringUtils.isEmpty(knownHostsFile)) {
|
||||
jSch.setKnownHosts(knownHostsFile);
|
||||
} else if (null != knownHostsInputStream) {
|
||||
}
|
||||
else if (null != knownHostsInputStream) {
|
||||
jSch.setKnownHosts(knownHostsInputStream);
|
||||
}
|
||||
|
||||
// private key
|
||||
if (!StringUtils.isEmpty(this.privateKey)) {
|
||||
if (!StringUtils.isEmpty(privateKeyPassphrase)) {
|
||||
jSch.addIdentity(this.privateKey, privateKeyPassphrase);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
jSch.addIdentity(this.privateKey);
|
||||
}
|
||||
}
|
||||
|
||||
session = jSch.getSession(userName, hostName, port);
|
||||
|
||||
this.session = jSch.getSession(userName, hostName, port);
|
||||
if (!StringUtils.isEmpty(userPassword)) {
|
||||
session.setPassword(userPassword);
|
||||
this.session.setPassword(userPassword);
|
||||
}
|
||||
|
||||
userInfo = new OptimisticUserInfoImpl(userPassword);
|
||||
session.setUserInfo(userInfo);
|
||||
session.connect();
|
||||
channel = (ChannelSftp) session.openChannel("sftp");
|
||||
this.userInfo = new OptimisticUserInfoImpl(userPassword);
|
||||
this.session.setUserInfo(userInfo);
|
||||
this.session.connect();
|
||||
this.channel = (ChannelSftp) this.session.openChannel("sftp");
|
||||
}
|
||||
|
||||
|
||||
public ChannelSftp getChannel() {
|
||||
return channel;
|
||||
}
|
||||
@@ -118,15 +119,17 @@ public class SftpSession {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* this is a simple, optimistic implementation of this interface. It simply returns in the positive where possible
|
||||
* and handles interactive authentication (ie, 'Please enter your password: ' prompts are dispatched automatically using this)
|
||||
*/
|
||||
private static class OptimisticUserInfoImpl implements UserInfo {
|
||||
private String pw;
|
||||
|
||||
private String password;
|
||||
|
||||
public OptimisticUserInfoImpl(String password) {
|
||||
this.pw = password;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPassphrase() {
|
||||
@@ -134,7 +137,7 @@ public class SftpSession {
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return pw;
|
||||
return password;
|
||||
}
|
||||
|
||||
public boolean promptPassphrase(String string) {
|
||||
@@ -152,4 +155,5 @@ public class SftpSession {
|
||||
public void showMessage(String string) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
@@ -20,43 +21,30 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Factories {@link SftpSession} instances. There are lots of ways to construct a
|
||||
* {@link SftpSession} instance, and not all of them are obvious. This factory
|
||||
* does its best to make it work.
|
||||
* Factory for creating {@link SftpSession} instances. There are lots of ways to construct a
|
||||
* {@link SftpSession} instance, and not all of them are obvious. This factory should help.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Mario Gray
|
||||
*/
|
||||
public class SftpSessionFactory implements FactoryBean<SftpSession>, InitializingBean {
|
||||
|
||||
private volatile String knownHosts;
|
||||
|
||||
private volatile String password;
|
||||
|
||||
private volatile String privateKey;
|
||||
|
||||
private volatile String privateKeyPassphrase;
|
||||
|
||||
private volatile String remoteHost;
|
||||
|
||||
private volatile String user;
|
||||
|
||||
private volatile int port = 22; // the default
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasText(this.remoteHost, "remoteHost can't be empty!");
|
||||
Assert.hasText(this.user, "user can't be empty!");
|
||||
Assert.state(StringUtils.hasText(this.password) || StringUtils.hasText(this.privateKey) || StringUtils.hasText(this.privateKeyPassphrase),
|
||||
"you must configure either a password or a private key and/or a private key passphrase!");
|
||||
Assert.state(this.port >= 0, "port must be a valid number! ");
|
||||
}
|
||||
|
||||
public SftpSession getObject() throws Exception {
|
||||
return new SftpSession(this.user, this.remoteHost, this.password, this.port, this.knownHosts, null, this.privateKey, this.privateKeyPassphrase);
|
||||
}
|
||||
|
||||
public Class<? extends SftpSession> getObjectType() {
|
||||
return SftpSession.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setKnownHosts(String knownHosts) {
|
||||
this.knownHosts = knownHosts;
|
||||
@@ -85,4 +73,25 @@ public class SftpSessionFactory implements FactoryBean<SftpSession>, Initializin
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasText(this.remoteHost, "remoteHost must not be empty");
|
||||
Assert.hasText(this.user, "user mut not be empty");
|
||||
Assert.state(StringUtils.hasText(this.password) || StringUtils.hasText(this.privateKey) || StringUtils.hasText(this.privateKeyPassphrase),
|
||||
"either a password or a private key and/or a private key passphrase is required");
|
||||
Assert.state(this.port >= 0, "port must be a positive number");
|
||||
}
|
||||
|
||||
public SftpSession getObject() throws Exception {
|
||||
return new SftpSession(this.user, this.remoteHost, this.password, this.port, this.knownHosts, null, this.privateKey, this.privateKeyPassphrase);
|
||||
}
|
||||
|
||||
public Class<? extends SftpSession> getObjectType() {
|
||||
return SftpSession.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,29 +13,30 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp;
|
||||
|
||||
/**
|
||||
* Holds instances of {@link SftpSession} since they're stateful
|
||||
* and might be in use while another run happens.
|
||||
* Holds instances of {@link SftpSession} since they are stateful
|
||||
* and might be in use while another operation runs.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public interface SftpSessionPool {
|
||||
|
||||
/**
|
||||
* this returns a session that can be used to connct to an sftp instance and perform operations
|
||||
* Returns a session that can be used to connect to an sftp instance and perform operations
|
||||
*
|
||||
* @return the session from the pool ready to be connected to.
|
||||
* @throws Exception thrown if theres any of the numerous faults possible when trying to connect to the remote
|
||||
* server
|
||||
* @throws Exception if any fault occurs when trying to connect to the remote server
|
||||
*/
|
||||
SftpSession getSession() throws Exception;
|
||||
|
||||
/**
|
||||
* Frees up the client. Im not sure what the meaningful semantics of this are. Perhaps it just calls <code>(session
|
||||
* ,channel).disconnect()</code> ?
|
||||
*
|
||||
* Releases the session.
|
||||
*
|
||||
* @param session the session to relinquish / renew
|
||||
*/
|
||||
void release(SftpSession session);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp.config;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
@@ -20,46 +21,32 @@ import org.springframework.integration.sftp.QueuedSftpSessionPool;
|
||||
import org.springframework.integration.sftp.SftpSendingMessageHandler;
|
||||
import org.springframework.integration.sftp.SftpSessionFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Supports the construction of a MessagHandler that knows how to take inbound #java.io.File objects and send them to a
|
||||
* remote destination.
|
||||
* Supports the construction of a MessagHandler that knows how to take inbound File objects
|
||||
* and send them to a remote destination.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpMessageSendingConsumerFactoryBean implements FactoryBean<SftpSendingMessageHandler> {
|
||||
|
||||
private String host;
|
||||
|
||||
private String keyFile;
|
||||
|
||||
private String keyFilePassword;
|
||||
|
||||
private String password;
|
||||
|
||||
private String remoteDirectory;
|
||||
|
||||
private String username;
|
||||
|
||||
private boolean autoCreateDirectories;
|
||||
|
||||
private int port;
|
||||
|
||||
private String charset;
|
||||
|
||||
public SftpSendingMessageHandler getObject() throws Exception {
|
||||
SftpSessionFactory sessionFactory = SftpSessionUtils.buildSftpSessionFactory(
|
||||
this.host, this.password, this.username, this.keyFile, this.keyFilePassword, this.port);
|
||||
|
||||
QueuedSftpSessionPool queuedSFTPSessionPool = new QueuedSftpSessionPool(15, sessionFactory);
|
||||
queuedSFTPSessionPool.afterPropertiesSet();
|
||||
|
||||
SftpSendingMessageHandler sftpSendingMessageHandler = new SftpSendingMessageHandler(queuedSFTPSessionPool);
|
||||
sftpSendingMessageHandler.setRemoteDirectory(this.remoteDirectory);
|
||||
sftpSendingMessageHandler.setCharset(this.charset);
|
||||
sftpSendingMessageHandler.afterPropertiesSet();
|
||||
|
||||
return sftpSendingMessageHandler;
|
||||
}
|
||||
|
||||
public Class<? extends SftpSendingMessageHandler> getObjectType() {
|
||||
return SftpSendingMessageHandler.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAutoCreateDirectories(final boolean autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
@@ -96,4 +83,25 @@ public class SftpMessageSendingConsumerFactoryBean implements FactoryBean<SftpSe
|
||||
public void setUsername(final String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public SftpSendingMessageHandler getObject() throws Exception {
|
||||
SftpSessionFactory sessionFactory = SftpSessionUtils.buildSftpSessionFactory(
|
||||
this.host, this.password, this.username, this.keyFile, this.keyFilePassword, this.port);
|
||||
QueuedSftpSessionPool queuedSFTPSessionPool = new QueuedSftpSessionPool(15, sessionFactory);
|
||||
queuedSFTPSessionPool.afterPropertiesSet();
|
||||
SftpSendingMessageHandler sftpSendingMessageHandler = new SftpSendingMessageHandler(queuedSFTPSessionPool);
|
||||
sftpSendingMessageHandler.setRemoteDirectory(this.remoteDirectory);
|
||||
sftpSendingMessageHandler.setCharset(this.charset);
|
||||
sftpSendingMessageHandler.afterPropertiesSet();
|
||||
return sftpSendingMessageHandler;
|
||||
}
|
||||
|
||||
public Class<? extends SftpSendingMessageHandler> getObjectType() {
|
||||
return SftpSendingMessageHandler.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,66 +13,63 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
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.NamespaceHandlerSupport;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
/**
|
||||
* Provides namespace support for using SFTP.
|
||||
* This is very largely based on the FTP support by Iwein Fuld.
|
||||
* This is largely based on the FTP support by Iwein Fuld.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SftpNamespaceHandler extends NamespaceHandlerSupport {
|
||||
public class SftpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("inbound-channel-adapter", new SFTPMessageSourceBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("outbound-channel-adapter", new SFTPMessageSendingConsumerBeanDefinitionParser());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures an object that can take inbound messages and send them.
|
||||
*/
|
||||
private static class SFTPMessageSendingConsumerBeanDefinitionParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SftpMessageSendingConsumerFactoryBean.class.getName());
|
||||
|
||||
for (String p : "auto-create-directories,username,password,host,port,key-file,key-file-password,remote-directory,charset".split(",")) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, p);
|
||||
}
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an object that can recieve files from a remote SFTP endpoint and broadcast their arrival to the
|
||||
* consumer
|
||||
* Configures an object that can receive files from a remote SFTP endpoint and broadcast their arrival to the consumer.
|
||||
*/
|
||||
private static class SFTPMessageSourceBeanDefinitionParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName());
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "filter");
|
||||
|
||||
for (String p : "filename-pattern,auto-create-directories,username,password,host,key-file,key-file-password,remote-directory,local-directory-path,auto-delete-remote-files-on-sync".split(",")) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, p);
|
||||
}
|
||||
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp.config;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
@@ -34,75 +35,79 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* Factory bean to hide the fairly complex configuration possibilities for an SFTP endpoint
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends AbstractFactoryBean<SftpInboundRemoteFileSystemSynchronizingMessageSource> implements ResourceLoaderAware {
|
||||
public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean
|
||||
extends AbstractFactoryBean<SftpInboundRemoteFileSystemSynchronizingMessageSource> implements ResourceLoaderAware {
|
||||
|
||||
private volatile ResourceLoader resourceLoader;
|
||||
|
||||
private volatile Resource localDirectoryResource;
|
||||
|
||||
private volatile String localDirectoryPath;
|
||||
|
||||
private volatile String autoCreateDirectories;
|
||||
|
||||
private volatile String autoDeleteRemoteFilesOnSync;
|
||||
|
||||
private volatile String filenamePattern;
|
||||
|
||||
private volatile EntryListFilter<ChannelSftp.LsEntry> filter;
|
||||
|
||||
private int port = 22;
|
||||
|
||||
private String host;
|
||||
|
||||
private String keyFile;
|
||||
|
||||
private String keyFilePassword;
|
||||
|
||||
private String remoteDirectory;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
public void setLocalDirectoryResource(Resource localDirectoryResource) {
|
||||
this.localDirectoryResource = localDirectoryResource;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setLocalDirectoryPath(String localDirectoryPath) {
|
||||
this.localDirectoryPath = localDirectoryPath;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoCreateDirectories(String autoCreateDirectories) {
|
||||
this.autoCreateDirectories = autoCreateDirectories;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) {
|
||||
this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFilter(EntryListFilter<ChannelSftp.LsEntry> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setKeyFile(String keyFile) {
|
||||
this.keyFile = keyFile;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setKeyFilePassword(String keyFilePassword) {
|
||||
this.keyFilePassword = keyFilePassword;
|
||||
}
|
||||
@@ -110,7 +115,6 @@ public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends A
|
||||
/**
|
||||
* Set the remote directory to synchronize with
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setRemoteDirectory(String remoteDirectory) {
|
||||
this.remoteDirectory = remoteDirectory;
|
||||
}
|
||||
@@ -118,7 +122,6 @@ public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends A
|
||||
/**
|
||||
* Set the user name to be used for authentication with the remote server
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
@@ -127,7 +130,6 @@ public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends A
|
||||
* Set the password to be used for authentication with the remote server
|
||||
* @param password
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
@@ -152,11 +154,9 @@ public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends A
|
||||
* @return Fully configured SftpInboundRemoteFileSystemSynchronizingMessageSource
|
||||
*/
|
||||
@Override
|
||||
protected SftpInboundRemoteFileSystemSynchronizingMessageSource createInstance()
|
||||
throws Exception {
|
||||
protected SftpInboundRemoteFileSystemSynchronizingMessageSource createInstance() throws Exception {
|
||||
boolean autoCreatDirs = Boolean.parseBoolean(this.autoCreateDirectories);
|
||||
boolean ackRemoteDir = Boolean.parseBoolean(this.autoDeleteRemoteFilesOnSync);
|
||||
|
||||
SftpInboundRemoteFileSystemSynchronizingMessageSource sftpMsgSrc = new SftpInboundRemoteFileSystemSynchronizingMessageSource();
|
||||
sftpMsgSrc.setAutoCreateDirectories(autoCreatDirs);
|
||||
|
||||
@@ -166,27 +166,22 @@ public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends A
|
||||
File sftpTmp = new File(tmp, "sftpInbound");
|
||||
this.localDirectoryPath = "file://" + sftpTmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
this.localDirectoryResource = this.resourceFromString(localDirectoryPath);
|
||||
|
||||
// remote predicates
|
||||
SftpEntryNamer sftpEntryNamer = new SftpEntryNamer();
|
||||
CompositeEntryListFilter<ChannelSftp.LsEntry> compositeFtpFileListFilter = new CompositeEntryListFilter<ChannelSftp.LsEntry>();
|
||||
|
||||
if (StringUtils.hasText(this.filenamePattern)) {
|
||||
PatternMatchingEntryListFilter<ChannelSftp.LsEntry> ftpFilePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter<ChannelSftp.LsEntry>(sftpEntryNamer, filenamePattern);
|
||||
compositeFtpFileListFilter.addFilter(ftpFilePatternMatchingEntryListFilter);
|
||||
}
|
||||
|
||||
if (this.filter != null) {
|
||||
compositeFtpFileListFilter.addFilter(this.filter);
|
||||
}
|
||||
|
||||
this.filter = compositeFtpFileListFilter;
|
||||
|
||||
// pools
|
||||
SftpSessionFactory sessionFactory = SftpSessionUtils.buildSftpSessionFactory(this.host, this.password, this.username, this.keyFile, this.keyFilePassword, this.port);
|
||||
|
||||
QueuedSftpSessionPool pool = new QueuedSftpSessionPool(15, sessionFactory);
|
||||
pool.afterPropertiesSet();
|
||||
|
||||
@@ -197,8 +192,8 @@ public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends A
|
||||
sftpSync.setFilter(compositeFtpFileListFilter);
|
||||
sftpSync.setBeanFactory(this.getBeanFactory());
|
||||
sftpSync.setRemotePath(this.remoteDirectory);
|
||||
sftpSync.afterPropertiesSet(); // todo is this correct ?
|
||||
sftpSync.start(); //todo
|
||||
sftpSync.afterPropertiesSet();
|
||||
sftpSync.start();
|
||||
|
||||
sftpMsgSrc.setRemotePredicate(compositeFtpFileListFilter);
|
||||
sftpMsgSrc.setSynchronizer(sftpSync);
|
||||
@@ -209,14 +204,13 @@ public class SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends A
|
||||
sftpMsgSrc.setAutoStartup(true);
|
||||
sftpMsgSrc.afterPropertiesSet();
|
||||
sftpMsgSrc.start();
|
||||
|
||||
return sftpMsgSrc;
|
||||
}
|
||||
|
||||
private Resource resourceFromString(String path) {
|
||||
ResourceEditor resourceEditor = new ResourceEditor(this.resourceLoader);
|
||||
resourceEditor.setAsText(path);
|
||||
|
||||
return (Resource) resourceEditor.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,17 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp.config;
|
||||
|
||||
import org.springframework.integration.sftp.SftpSessionFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Provides a single place to handle this tedious chore.
|
||||
* Utility methods for SFTP Session management.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpSessionUtils {
|
||||
public abstract class SftpSessionUtils {
|
||||
|
||||
/**
|
||||
* This method hides the minutae required to build an #SftpSessionFactory.
|
||||
*
|
||||
@@ -39,8 +40,7 @@ public class SftpSessionUtils {
|
||||
* commands against a remote SFTP/SSH filesystem
|
||||
* @throws Exception thrown in case of darned near <em>anything</em>
|
||||
*/
|
||||
public static SftpSessionFactory buildSftpSessionFactory(String host, String pw, String usr, String pvKey, String pvKeyPass, int port)
|
||||
throws Exception {
|
||||
public static SftpSessionFactory buildSftpSessionFactory(String host, String pw, String usr, String pvKey, String pvKeyPass, int port) throws Exception {
|
||||
SftpSessionFactory sftpSessionFactory = new SftpSessionFactory();
|
||||
sftpSessionFactory.setPassword(pw);
|
||||
sftpSessionFactory.setPort(port);
|
||||
@@ -49,7 +49,7 @@ public class SftpSessionUtils {
|
||||
sftpSessionFactory.setPrivateKey(pvKey);
|
||||
sftpSessionFactory.setPrivateKeyPassphrase(pvKeyPass);
|
||||
sftpSessionFactory.afterPropertiesSet();
|
||||
|
||||
return sftpSessionFactory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.sftp.impl;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
@@ -34,13 +35,13 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* This handles the synchronization between a remote SFTP endpoint and a local mount
|
||||
* Gandles the synchronization between a remote SFTP endpoint and a local mount.
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
public class SftpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemoteFileSystemSychronizer<ChannelSftp.LsEntry> {
|
||||
|
||||
/**
|
||||
* the path on the remote mount
|
||||
*/
|
||||
@@ -51,62 +52,61 @@ public class SftpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemo
|
||||
*/
|
||||
private volatile SftpSessionPool clientPool;
|
||||
|
||||
|
||||
public void setRemotePath(String remotePath) {
|
||||
this.remotePath = remotePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.clientPool, "'clientPool' can't be null");
|
||||
Assert.notNull(this.remotePath, "'remotePath' can't be null");
|
||||
if (this.shouldDeleteSourceFile) {
|
||||
this.entryAcknowledgmentStrategy = new DeletionEntryAcknowledgmentStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
@Required
|
||||
public void setClientPool(SftpSessionPool clientPool) {
|
||||
this.clientPool = clientPool;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ignored")
|
||||
private boolean copyFromRemoteToLocalDirectory(SftpSession sftpSession, ChannelSftp.LsEntry entry, Resource localDir)
|
||||
throws Exception {
|
||||
@Override
|
||||
protected Trigger getTrigger() {
|
||||
return new PeriodicTrigger(10 * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.clientPool, "'clientPool' must not be null");
|
||||
Assert.notNull(this.remotePath, "'remotePath' must not be null");
|
||||
if (this.shouldDeleteSourceFile) {
|
||||
this.entryAcknowledgmentStrategy = new DeletionEntryAcknowledgmentStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean copyFromRemoteToLocalDirectory(SftpSession sftpSession, ChannelSftp.LsEntry entry, Resource localDir) throws Exception {
|
||||
File fileForLocalDir = localDir.getFile();
|
||||
|
||||
File localFile = new File(fileForLocalDir, entry.getFilename());
|
||||
|
||||
if (!localFile.exists()) {
|
||||
InputStream in = null;
|
||||
FileOutputStream fileOutputStream = null;
|
||||
|
||||
try {
|
||||
File tmpLocalTarget = new File(localFile.getAbsolutePath() +
|
||||
AbstractInboundRemoteFileSystemSynchronizingMessageSource.INCOMPLETE_EXTENSION);
|
||||
|
||||
fileOutputStream = new FileOutputStream(tmpLocalTarget);
|
||||
|
||||
String remoteFqPath = this.remotePath + "/" + entry.getFilename();
|
||||
in = sftpSession.getChannel().get(remoteFqPath);
|
||||
try {
|
||||
IOUtils.copy(in, fileOutputStream);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
IOUtils.closeQuietly(fileOutputStream);
|
||||
}
|
||||
|
||||
if (tmpLocalTarget.renameTo(localFile)) {
|
||||
this.acknowledge(sftpSession, entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable th) {
|
||||
logger.error("exception thrown in #copyFromRemoteToLocalDirectory", th);
|
||||
}
|
||||
} else {
|
||||
catch (Throwable th) {
|
||||
logger.error("failure occurred while copying from remote to local directory", th);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,47 +114,39 @@ public class SftpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemo
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void syncRemoteToLocalFileSystem() throws Exception {
|
||||
SftpSession session = null;
|
||||
|
||||
try {
|
||||
session = clientPool.getSession();
|
||||
session.start();
|
||||
|
||||
ChannelSftp channelSftp = session.getChannel();
|
||||
Collection<ChannelSftp.LsEntry> beforeFilter = channelSftp.ls(remotePath);
|
||||
ChannelSftp.LsEntry[] entries = (beforeFilter == null) ? new ChannelSftp.LsEntry[0] : beforeFilter.toArray(new ChannelSftp.LsEntry[beforeFilter.size()]);
|
||||
Collection<ChannelSftp.LsEntry> files = this.filter.filterEntries(entries);
|
||||
|
||||
for (ChannelSftp.LsEntry lsEntry : files) {
|
||||
if ((lsEntry != null) && !lsEntry.getAttrs().isDir() && !lsEntry.getAttrs().isLink()) {
|
||||
copyFromRemoteToLocalDirectory(session, lsEntry, this.localDirectory);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessagingException("couldn't synchronize remote to local directory", e);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if ((session != null) && (clientPool != null)) {
|
||||
clientPool.release(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Trigger getTrigger() {
|
||||
return new PeriodicTrigger(10 * 1000);
|
||||
}
|
||||
|
||||
class DeletionEntryAcknowledgmentStrategy implements AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy<ChannelSftp.LsEntry> {
|
||||
public void acknowledge(Object useful, ChannelSftp.LsEntry msg)
|
||||
throws Exception {
|
||||
private class DeletionEntryAcknowledgmentStrategy implements AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy<ChannelSftp.LsEntry> {
|
||||
|
||||
public void acknowledge(Object useful, ChannelSftp.LsEntry msg) throws Exception {
|
||||
SftpSession sftpSession = (SftpSession) useful;
|
||||
|
||||
String remoteFqPath = remotePath + "/" + msg.getFilename();
|
||||
|
||||
sftpSession.getChannel().rm(remoteFqPath);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("deleted " + msg.getFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,8 +18,8 @@ package org.springframework.integration.stream.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -32,7 +32,7 @@ import org.springframework.util.StringUtils;
|
||||
public class ConsoleInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected String parseSource(Element element, ParserContext parserContext) {
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.stream.CharacterStreamReadingMessageSource");
|
||||
builder.setFactoryMethod("stdin");
|
||||
@@ -40,7 +40,7 @@ public class ConsoleInboundChannelAdapterParser extends AbstractPollingInboundCh
|
||||
if (StringUtils.hasText(charsetName)) {
|
||||
builder.addConstructorArgValue(charsetName);
|
||||
}
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +77,12 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
|
||||
@@ -13,32 +13,49 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.twitter.config;
|
||||
|
||||
import static org.springframework.integration.twitter.config.TwitterNamespaceHandler.BASE_PACKAGE;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.w3c.dom.Element;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.integration.twitter.config.TwitterNamespaceHandler.BASE_PACKAGE;
|
||||
/**
|
||||
* Parser for 'twitter-connection' element
|
||||
* Parser for the 'twitter-connection' element.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ConnectionParser extends AbstractSingleBeanDefinitionParser {
|
||||
@Override
|
||||
protected String getBeanClassName(Element element) {
|
||||
return BASE_PACKAGE + ".oauth.OAuthConfigurationFactoryBean";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
TwitterNamespaceHandler.configureTwitterConnection(element, parserContext, builder);
|
||||
}
|
||||
@Override
|
||||
protected String getBeanClassName(Element element) {
|
||||
return BASE_PACKAGE + ".oauth.OAuthConfigurationFactoryBean";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateIdAsFallback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String ref = element.getAttribute("twitter-connection");
|
||||
if (StringUtils.hasText(ref)) {
|
||||
builder.addPropertyReference("twitterConnection", ref);
|
||||
}
|
||||
else {
|
||||
for (String attribute : new String[] { "consumer-key", "consumer-secret", "access-token", "access-token-secret" }) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateIdAsFallback() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.twitter.config;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import static org.springframework.integration.twitter.config.TwitterNamespaceHandler.BASE_PACKAGE;
|
||||
|
||||
/**
|
||||
* Parser for 'outbound-dm-channel-adapter' element
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
public class OutboundDirectMessageMessageHandlerParser extends AbstractOutboundChannelAdapterParser {
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
BASE_PACKAGE + ".outbound.OutboundDirectMessageMessageHandler" );
|
||||
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration");
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user