我正在尝试在Spring Integration中运行Dynamic FTP示例。但是将模式改为SFTP。在运行测试时,由于以下消息显示表达式评估失败
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:来自类路径资源的XML文档中的第30行[META-INF / spring / integration / dynamic-ftp-outbound-adapter-context.xml]无效;嵌套异常是org.xml.sax.SAXParseException; lineNumber:30; columnNumber:31; cvc-complex-type.2.4.c:匹配的通配符是strict,但是找不到元素'int-sftp:outbound-channel-adapter'的声明。
我的文件如下
**pom.xml**
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.integration.samples</groupId>
<artifactId>DynamicFtp</artifactId>
<version>0.0.1</version>
<name>Dynamic FTP </name>
<description>Dynamic FTP</description>
<!-- <repositories>
<repository>
<id>repo.spring.io.milestone</id>
<name>Spring Framework Maven Milestone Repository</name>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
</repositories> -->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-sftp</artifactId>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ftp</artifactId>
<version>4.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.1.1.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
**DynamicFtpChannelResolver**
public class DynamicFtpChannelResolver {
//In production environment this value will be significantly higher
//This is just to demonstrate the concept of limiting the max number of
//Dynamically created application contexts we'll hold in memory when we execute
//the code from a junit
public static final int MAX_CACHE_SIZE = 2;
private final LinkedHashMap<String, MessageChannel> channels =
new LinkedHashMap<String, MessageChannel>() {
private static final long serialVersionUID = 1L;
@Override
protected boolean removeEldestEntry(
Entry<String, MessageChannel> eldest) {
//This returning true means the least recently used
//channel and its application context will be closed and removed
boolean remove = size() > MAX_CACHE_SIZE;
if(remove) {
MessageChannel channel = eldest.getValue();
ConfigurableApplicationContext ctx = contexts.get(channel);
if(ctx != null) { //shouldn't be null ideally
ctx.close();
contexts.remove(channel);
}
}
return remove;
}
};
private final Map<MessageChannel, ConfigurableApplicationContext> contexts =
new HashMap<MessageChannel, ConfigurableApplicationContext>();
/**
* Resolve a customer to a channel, where each customer gets a private
* application context and the channel is the inbound channel to that
* application context.
*
* @param customer
* @return a channel
*/
public MessageChannel resolve(String customer) {
MessageChannel channel = this.channels.get(customer);
if (channel == null) {
channel = createNewCustomerChannel(customer);
}
return channel;
}
private synchronized MessageChannel createNewCustomerChannel(String customer) {
MessageChannel channel = this.channels.get(customer);
if (channel == null) {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] { "/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml" },
false);
this.setEnvironmentForCustomer(ctx, customer);
ctx.refresh();
channel = ctx.getBean("toFtpChannel", MessageChannel.class);
this.channels.put(customer, channel);
//Will works as the same reference is presented always
this.contexts.put(channel, ctx);
}
return channel;
}
/**
* Use Spring 3.1. environment support to set properties for the
* customer-specific application context.
*
* @param ctx
* @param customer
*/
private void setEnvironmentForCustomer(ConfigurableApplicationContext ctx,
String customer) {
StandardEnvironment env = new StandardEnvironment();
Properties props = new Properties();
// populate properties for customer
// props.setProperty("host", "host.for." + customer);
// props.setProperty("user", "user");
// props.setProperty("password", "password");
props.setProperty("host", "172.18.554.23");
props.setProperty("user", "iuser");
props.setProperty("port", "22");
props.setProperty("password", "ipwddd");
props.setProperty("remote.directory", "/u01/dpp/dppuser/ftp/");
PropertiesPropertySource pps = new PropertiesPropertySource("ftpprops", props);
env.getPropertySources().addLast(pps);
ctx.setEnvironment(env);
System.out.println("the properties are set" +pps);
}
}
**dynamic-ftp-outbound-context.xml**
<?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-ftp="http://www.springframework.org/schema/integration/ftp"
xmlns:int-sftp="http://www.springframework.org/schema/integration /sftp"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/ftp/spring-integration-sftp.xsd">
<context:property-placeholder />
<bean id="ftpClientFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${host}"/>
<property name="port" value="${port}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
<int:channel id="toFtpChannel"/>
<int-sftp:outbound-channel-adapter id="ftpOutbound"
channel="toFtpChannel"
remote-directory="${remote.directory}"
session-factory="sftpSessionFactory"
mode="REPLACE"
remote-file-separator="/">
<int:poller fixed-rate="100" time-unit="MILLISECONDS" max-messages-per-poll="100"/>
</int-sftp:outbound-channel-adapter>
<bean id="sftpSessionFactory"
class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="ftpClientFactory" />
</bean>
</beans>
**dyamic-ftp-ountbound-sample-context.xml**
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="channelResolver" class="com.tcs.DynamicFtp.DynamicFtpChannelResolver" />
<int:channel id="toDynRouter" />
<int:router input-channel="toDynRouter"
expression="@channelResolver.resolve(headers['customer'])"/>
</beans>
**The test file**
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.net.UnknownHostException;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessagingException;
public class FtpOutboundChannelAdapterSample {
@Test
public void runDemo() throws Exception{
ConfigurableApplicationContext ctx =
new ClassPathXmlApplicationContext("META-INF/spring/integration /DynamicFtpOutboundChannelAdapterSample-context.xml");
MessageChannel channel = ctx.getBean("toDynRouter", MessageChannel.class);
File file = File.createTempFile("temp", "txt");
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
bw.write("This is the temporary file content");
bw.close();
Message<File> message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust1")
.build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust1"));
}
// send another so we can see in the log we don't create the ac again.
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust1"));
}
// send to a different customer; again, check the log to see a new ac is built
message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust2").build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust2"));
}
// send to a different customer; again, check the log to see a new ac is built
//and the first one created (cust1) should be closed and removed as per the max cache size restriction
message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust3").build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(), instanceOf(UnknownHostException.class));
assertTrue(e.getCause().getCause().getCause().getMessage().startsWith("host.for.cust3"));
}
//send to cust1 again, since this one has been invalidated before, we should
//see a new ac created (with ac of cust2 destroyed and removed)
message = MessageBuilder.withPayload(file)
.setHeader("customer", "cust1").build();
try {
channel.send(message);
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getCause(),
instanceOf(UnknownHostException.class));
assertEquals("host.for.cust1",
e.getCause().getCause().getCause().getMessage());
}
ctx.close();
}
}
答案 0 :(得分:0)
您的xmlns
配置错误
xmlns:int-sftp="http://www.springframework.org/schema/integration /sftp"
其中不应有空格。
修改强>
您还有其他错误:
http://www.springframework.org/schema/integration/ftp/spring-integration-sftp.xsd">
应该是
http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
我通常使用IDE来管理命名空间的东西;正如您所发现的那样,手动执行此操作非常容易出错。
我刚刚在本地进行了测试,没有任何问题。但是,这个
<property name="username" value="${user}"/>
应该失败,因为sftp工厂的属性为user
;还有为什么要添加<poller/>
?
频道不可轮询。