Add Apache MINA SftpEventListener
- republish certain events as `ApplicationEvent`s. * * Add ApacheMinaFtplet to provide the same functionality with FTP * Fix typo * * Polishing javadocs and event toString() methods
This commit is contained in:
committed by
Artem Bilan
parent
b1dfb7bfa3
commit
cd0f56bc87
@@ -446,9 +446,9 @@ project('spring-integration-ftp') {
|
||||
compile project(":spring-integration-file")
|
||||
compile "commons-net:commons-net:$commonsNetVersion"
|
||||
compile "org.springframework:spring-context-support:$springVersion"
|
||||
compile("javax.activation:javax.activation-api:$javaxActivationVersion", optional)
|
||||
compile ("javax.activation:javax.activation-api:$javaxActivationVersion", optional)
|
||||
compile ("org.apache.ftpserver:ftpserver-core:$ftpServerVersion", optional)
|
||||
|
||||
testCompile "org.apache.ftpserver:ftpserver-core:$ftpServerVersion"
|
||||
testCompile project(":spring-integration-file").sourceSets.test.output
|
||||
}
|
||||
}
|
||||
@@ -661,10 +661,11 @@ project('spring-integration-sftp') {
|
||||
compile project(":spring-integration-stream")
|
||||
compile "com.jcraft:jsch:$jschVersion"
|
||||
compile "org.springframework:spring-context-support:$springVersion"
|
||||
compile("javax.activation:javax.activation-api:$javaxActivationVersion", optional)
|
||||
compile ("javax.activation:javax.activation-api:$javaxActivationVersion", optional)
|
||||
compile ("org.apache.sshd:sshd-sftp:$apacheSshdVersion", optional)
|
||||
|
||||
testCompile "org.apache.sshd:sshd-core:$apacheSshdVersion"
|
||||
testCompile "org.apache.sshd:sshd-sftp:$apacheSshdVersion"
|
||||
testCompile project(":spring-integration-event")
|
||||
testCompile project(":spring-integration-file").sourceSets.test.output
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.integration.events;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
@@ -32,14 +33,13 @@ import org.springframework.context.ApplicationEvent;
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class IntegrationEvent extends ApplicationEvent {
|
||||
|
||||
private final Throwable cause;
|
||||
protected final Throwable cause; // NOSONAR protected final
|
||||
|
||||
public IntegrationEvent(Object source) {
|
||||
super(source);
|
||||
this.cause = null;
|
||||
this(source, null);
|
||||
}
|
||||
|
||||
public IntegrationEvent(Object source, Throwable cause) {
|
||||
public IntegrationEvent(Object source, @Nullable Throwable cause) {
|
||||
super(source);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.file.remote.server;
|
||||
|
||||
import org.springframework.integration.events.IntegrationEvent;
|
||||
|
||||
/**
|
||||
* Base class for file server events. Typically, the source for these events will be some
|
||||
* kind of client/server session object containing information such as the client's ip
|
||||
* address.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class FileServerEvent extends IntegrationEvent {
|
||||
|
||||
public FileServerEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public FileServerEvent(Object source, Throwable cause) {
|
||||
super(source, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes related to file servers.
|
||||
*/
|
||||
package org.springframework.integration.file.remote.server;
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
import org.springframework.integration.file.remote.server.FileServerEvent;
|
||||
|
||||
/**
|
||||
* {@code ApplicationEvent} generated from Apache Mina ftp events.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public abstract class ApacheMinaFtpEvent extends FileServerEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ApacheMinaFtpEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public ApacheMinaFtpEvent(Object source, Throwable cause) {
|
||||
super(source, cause);
|
||||
}
|
||||
|
||||
public FtpSession getSession() {
|
||||
return (FtpSession) source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " [clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.ftpserver.ftplet.DefaultFtplet;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.apache.ftpserver.ftplet.FtpRequest;
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
import org.apache.ftpserver.ftplet.FtpletResult;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A listener for FTP events emitted by an Apache Mina ftp server.
|
||||
* It emits selected events as Spring Framework {@code ApplicationEvent}s
|
||||
* which are subclasses of {@link ApacheMinaFtpEvent}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class ApacheMinaFtplet extends DefaultFtplet
|
||||
implements ApplicationEventPublisherAware, BeanNameAware, InitializingBean {
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private String beanName;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
protected ApplicationEventPublisher getApplicationEventPublisher() {
|
||||
return this.applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
public String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.state(this.applicationEventPublisher != null, "An ApplicationEventPublisher is required");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onConnect(FtpSession session) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new SessionOpenedEvent(session));
|
||||
return super.onConnect(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onDisconnect(FtpSession session) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new SessionClosedEvent(session));
|
||||
return super.onDisconnect(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onDeleteEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new PathRemovedEvent(session, request, false));
|
||||
return super.onDeleteEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new FileWrittenEvent(session, request, false));
|
||||
return super.onUploadEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onRmdirEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new PathRemovedEvent(session, request, true));
|
||||
return super.onRmdirEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onMkdirEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new DirectoryCreatedEvent(session, request));
|
||||
return super.onMkdirEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onAppendEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new FileWrittenEvent(session, request, false));
|
||||
return super.onAppendEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onRenameEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
this.applicationEventPublisher.publishEvent(new PathMovedEvent(session, request));
|
||||
return super.onRenameEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApacheMinaSftpEventListener [beanName=" + this.beanName + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpRequest;
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
/**
|
||||
* An event emitted when a directory is created.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class DirectoryCreatedEvent extends FtpRequestEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DirectoryCreatedEvent(FtpSession source, FtpRequest request) {
|
||||
super(source, request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpRequest;
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
/**
|
||||
* An event that is emitted when a file is written.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class FileWrittenEvent extends FtpRequestEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final boolean append;
|
||||
|
||||
public FileWrittenEvent(FtpSession source, FtpRequest request, boolean append) {
|
||||
super(source, request);
|
||||
this.append = append;
|
||||
}
|
||||
|
||||
public boolean isAppend() {
|
||||
return this.append;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FileWrittenEvent [append=" + this.append
|
||||
+ ", request=" + this.request
|
||||
+ ", clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpRequest;
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
/**
|
||||
* Base class for all events having an {@link FtpRequest}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public abstract class FtpRequestEvent extends ApacheMinaFtpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected final FtpRequest request; //NOSONAR protected final
|
||||
|
||||
public FtpRequestEvent(FtpSession source, FtpRequest request) {
|
||||
super(source);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public FtpRequest getRequest() {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " [request=" + this.request
|
||||
+ ", clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpRequest;
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
/**
|
||||
* An event emitted when a path is moved.
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class PathMovedEvent extends FtpRequestEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PathMovedEvent(FtpSession source, FtpRequest request) {
|
||||
super(source, request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpRequest;
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
/**
|
||||
* An event emitted when a file or directory is removed.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class PathRemovedEvent extends FtpRequestEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final boolean isDirectory;
|
||||
|
||||
public PathRemovedEvent(FtpSession source, FtpRequest request, boolean isDirectory) {
|
||||
super(source, request);
|
||||
this.isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return this.isDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathRemovedEvent [isDirectory=" + this.isDirectory
|
||||
+ ", request=" + this.request
|
||||
+ ", clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
/**
|
||||
* An event emitted when a session is closed.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class SessionClosedEvent extends ApacheMinaFtpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SessionClosedEvent(FtpSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.server;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
|
||||
/**
|
||||
* An event emitted when a session is opened.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class SessionOpenedEvent extends ApacheMinaFtpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SessionOpenedEvent(FtpSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes related to FTP servers.
|
||||
*/
|
||||
package org.springframework.integration.ftp.server;
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
@@ -39,6 +41,7 @@ import org.junit.BeforeClass;
|
||||
import org.springframework.integration.file.remote.RemoteFileTestSupport;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.ftp.server.ApacheMinaFtplet;
|
||||
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
|
||||
|
||||
/**
|
||||
@@ -51,6 +54,8 @@ import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
|
||||
*/
|
||||
public class FtpTestSupport extends RemoteFileTestSupport {
|
||||
|
||||
private static final ApacheMinaFtplet FTPLET = new ApacheMinaFtplet();
|
||||
|
||||
private static volatile FtpServer server;
|
||||
|
||||
@BeforeClass
|
||||
@@ -61,7 +66,10 @@ public class FtpTestSupport extends RemoteFileTestSupport {
|
||||
ListenerFactory factory = new ListenerFactory();
|
||||
factory.setPort(0);
|
||||
serverFactory.addListener("default", factory.createListener());
|
||||
|
||||
serverFactory.setFtplets(new HashMap<>(Collections.singletonMap("springFtplet", FTPLET)));
|
||||
FTPLET.setApplicationEventPublisher(ev -> {
|
||||
// no-op
|
||||
});
|
||||
server = serverFactory.createServer();
|
||||
server.start();
|
||||
|
||||
@@ -94,6 +102,10 @@ public class FtpTestSupport extends RemoteFileTestSupport {
|
||||
return sf;
|
||||
}
|
||||
|
||||
protected static ApacheMinaFtplet ftplet() {
|
||||
return FTPLET;
|
||||
}
|
||||
|
||||
private static class TestUserManager implements UserManager {
|
||||
|
||||
private final BaseUser testUser;
|
||||
|
||||
@@ -28,11 +28,14 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
@@ -47,7 +50,9 @@ import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
@@ -58,10 +63,18 @@ import org.springframework.integration.file.remote.InputStreamCallback;
|
||||
import org.springframework.integration.file.remote.MessageSessionCallback;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Option;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.ftp.FtpTestSupport;
|
||||
import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
|
||||
import org.springframework.integration.ftp.server.ApacheMinaFtpEvent;
|
||||
import org.springframework.integration.ftp.server.DirectoryCreatedEvent;
|
||||
import org.springframework.integration.ftp.server.FileWrittenEvent;
|
||||
import org.springframework.integration.ftp.server.PathMovedEvent;
|
||||
import org.springframework.integration.ftp.server.PathRemovedEvent;
|
||||
import org.springframework.integration.ftp.server.SessionClosedEvent;
|
||||
import org.springframework.integration.ftp.server.SessionOpenedEvent;
|
||||
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.PartialSuccessException;
|
||||
@@ -70,6 +83,7 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -142,9 +156,15 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
@Autowired
|
||||
private DirectChannel inboundNlst;
|
||||
|
||||
@Autowired
|
||||
private SessionFactory<FTPFile> sessionFactory;
|
||||
|
||||
@Autowired
|
||||
private SourcePollingChannelAdapter ftpInbound;
|
||||
|
||||
@Autowired
|
||||
private FtpRemoteFileTemplate template;
|
||||
|
||||
@Autowired
|
||||
private Config config;
|
||||
|
||||
@@ -673,6 +693,65 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
this.ftpInbound.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allEvents() throws InterruptedException {
|
||||
resetSessionCache();
|
||||
this.config.events.clear();
|
||||
this.config.latch = new CountDownLatch(1);
|
||||
this.template.execute(session -> {
|
||||
assertThat(session.mkdir("/ftpTarget/allEventsDir")).isTrue();
|
||||
session.write(new ByteArrayInputStream("foo".getBytes()), "/ftpTarget/allEventsDir/file.txt");
|
||||
session.append(new ByteArrayInputStream("bar".getBytes()), "/ftpTarget/allEventsDir/file.txt");
|
||||
session.rename("/ftpTarget/allEventsDir/file.txt", "/ftpTarget/allEventsDir/file2.txt");
|
||||
assertThat(session.remove("/ftpTarget/allEventsDir/file2.txt")).isTrue();
|
||||
session.rename("/ftpTarget/allEventsDir", "/ftpTarget/allEventsDir2");
|
||||
session.rmdir("/ftpTarget/allEventsDir2");
|
||||
return null;
|
||||
});
|
||||
resetSessionCache();
|
||||
assertThat(this.config.latch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(this.config.events).hasSize(11);
|
||||
assertThat(this.config.events.get(0)).isInstanceOf(SessionOpenedEvent.class);
|
||||
assertThat(this.config.events.get(1)).isInstanceOf(DirectoryCreatedEvent.class);
|
||||
DirectoryCreatedEvent dce = (DirectoryCreatedEvent) this.config.events.get(1);
|
||||
assertThat(dce.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir");
|
||||
assertThat(this.config.events.get(2)).isInstanceOf(FileWrittenEvent.class);
|
||||
FileWrittenEvent fwe = (FileWrittenEvent) this.config.events.get(2);
|
||||
assertThat(fwe.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir/file.txt");
|
||||
assertThat(this.config.events.get(3)).isInstanceOf(FileWrittenEvent.class);
|
||||
fwe = (FileWrittenEvent) this.config.events.get(3);
|
||||
assertThat(fwe.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir/file.txt");
|
||||
assertThat(this.config.events.get(4)).isInstanceOf(PathRemovedEvent.class);
|
||||
PathRemovedEvent pre = (PathRemovedEvent) this.config.events.get(4);
|
||||
assertThat(pre.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir/file2.txt");
|
||||
assertThat(pre.isDirectory()).isFalse(); // implicit DELE before RNTO
|
||||
assertThat(this.config.events.get(5)).isInstanceOf(PathMovedEvent.class);
|
||||
PathMovedEvent pme = (PathMovedEvent) this.config.events.get(5);
|
||||
assertThat(pme.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir/file2.txt");
|
||||
assertThat(this.config.events.get(6)).isInstanceOf(PathRemovedEvent.class);
|
||||
pre = (PathRemovedEvent) this.config.events.get(6);
|
||||
assertThat(pre.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir/file2.txt");
|
||||
assertThat(pre.isDirectory()).isFalse();
|
||||
assertThat(this.config.events.get(7)).isInstanceOf(PathRemovedEvent.class);
|
||||
pre = (PathRemovedEvent) this.config.events.get(7);
|
||||
assertThat(pre.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir2");
|
||||
assertThat(pre.isDirectory()).isFalse(); // implicit DELE before RNTO
|
||||
assertThat(this.config.events.get(8)).isInstanceOf(PathMovedEvent.class);
|
||||
pme = (PathMovedEvent) this.config.events.get(8);
|
||||
assertThat(pme.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir2");
|
||||
assertThat(this.config.events.get(9)).isInstanceOf(PathRemovedEvent.class);
|
||||
pre = (PathRemovedEvent) this.config.events.get(9);
|
||||
assertThat(pre.getRequest().getArgument()).isEqualTo("/ftpTarget/allEventsDir2");
|
||||
assertThat(pre.isDirectory()).isTrue();
|
||||
assertThat(this.config.events.get(10)).isInstanceOf(SessionClosedEvent.class);
|
||||
this.config.events.clear();
|
||||
this.config.latch = null;
|
||||
}
|
||||
|
||||
private void resetSessionCache() {
|
||||
((CachingSessionFactory<?>) this.sessionFactory).resetCache();
|
||||
}
|
||||
|
||||
public static class SortingFileListFilter implements FileListFilter<File> {
|
||||
|
||||
@Override
|
||||
@@ -705,12 +784,18 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
public static class Config {
|
||||
|
||||
final List<ApacheMinaFtpEvent> events = new ArrayList<>();
|
||||
|
||||
private volatile String targetLocalDirectoryName;
|
||||
|
||||
private volatile CountDownLatch latch;
|
||||
|
||||
@Bean
|
||||
public SessionFactory<FTPFile> ftpSessionFactory() {
|
||||
public SessionFactory<FTPFile> ftpSessionFactory(ApplicationContext context) {
|
||||
FtpServerOutboundTests.ftplet().setApplicationEventPublisher(context);
|
||||
return FtpServerOutboundTests.sessionFactory();
|
||||
}
|
||||
|
||||
@@ -718,6 +803,23 @@ public class FtpServerOutboundTests extends FtpTestSupport {
|
||||
return this.targetLocalDirectoryName;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FtpRemoteFileTemplate template(SessionFactory<FTPFile> sf) {
|
||||
return new FtpRemoteFileTemplate(sf);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleEvent(ApacheMinaFtpEvent event) {
|
||||
if (this.latch != null) {
|
||||
if (this.events.size() > 0 || event instanceof SessionOpenedEvent) {
|
||||
this.events.add(event);
|
||||
if (event instanceof SessionClosedEvent) {
|
||||
this.latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
|
||||
import org.springframework.integration.file.remote.server.FileServerEvent;
|
||||
|
||||
/**
|
||||
* {@code ApplicationEvent} generated from Apache Mina sftp events.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public abstract class ApacheMinaSftpEvent extends FileServerEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ApacheMinaSftpEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public ApacheMinaSftpEvent(Object source, Throwable cause) {
|
||||
super(source, cause);
|
||||
}
|
||||
|
||||
public ServerSession getSession() {
|
||||
return (ServerSession) source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
import org.apache.sshd.server.subsystem.sftp.FileHandle;
|
||||
import org.apache.sshd.server.subsystem.sftp.SftpEventListener;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A listener for SFTP events emitted by an Apache Mina sshd/sftp server.
|
||||
* It emits selected events as Spring Framework {@code ApplicationEvent}s
|
||||
* which are subclasses of {@link ApacheMinaSftpEvent}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class ApacheMinaSftpEventListener implements SftpEventListener, ApplicationEventPublisherAware, BeanNameAware, InitializingBean {
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private String beanName;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
protected ApplicationEventPublisher getApplicationEventPublisher() {
|
||||
return this.applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
public String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.state(this.applicationEventPublisher != null, "An ApplicationEventPublisher is required");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialized(ServerSession session, int version) {
|
||||
this.applicationEventPublisher.publishEvent(new SessionOpenedEvent(session, version));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroying(ServerSession session) throws IOException {
|
||||
this.applicationEventPublisher.publishEvent(new SessionClosedEvent(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(ServerSession session, Path path, Map<String, ?> attrs, Throwable thrown) {
|
||||
this.applicationEventPublisher.publishEvent(new DirectoryCreatedEvent(session, path, attrs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(ServerSession session, Path path, boolean isDirectory, Throwable thrown) {
|
||||
this.applicationEventPublisher.publishEvent(new PathRemovedEvent(session, path, isDirectory, thrown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void written(ServerSession session, String remoteHandle, FileHandle localHandle, long offset, byte[] data,
|
||||
int dataOffset, int dataLen, Throwable thrown) {
|
||||
|
||||
this.applicationEventPublisher.publishEvent(new FileWrittenEvent(session, remoteHandle, localHandle.getFile(),
|
||||
dataLen, thrown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moved(ServerSession session, Path srcPath, Path dstPath, Collection<CopyOption> opts,
|
||||
Throwable thrown) {
|
||||
|
||||
this.applicationEventPublisher.publishEvent(new PathMovedEvent(session, srcPath, dstPath, thrown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApacheMinaSftpEventListener [beanName=" + this.beanName + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An event emitted when a directory is created.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class DirectoryCreatedEvent extends ApacheMinaSftpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Path path;
|
||||
|
||||
private final Map<String, ?> attrs;
|
||||
|
||||
public DirectoryCreatedEvent(Object source, Path path, Map<String, ?> attrs) {
|
||||
super(source);
|
||||
this.path = path;
|
||||
this.attrs = attrs;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public Map<String, ?> getAttrs() {
|
||||
return this.attrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DirectoryCreatedEvent [path=" + this.path
|
||||
+ ", attrs=" + this.attrs
|
||||
+ ", clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An event that is emitted when a file is written.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class FileWrittenEvent extends ApacheMinaSftpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String remoteHandle;
|
||||
|
||||
private final Path file;
|
||||
|
||||
private final int dataLen;
|
||||
|
||||
public FileWrittenEvent(Object source, String remoteHandle, Path file, int dataLen, @Nullable Throwable thrown) {
|
||||
super(source, thrown);
|
||||
this.remoteHandle = remoteHandle;
|
||||
this.file = file;
|
||||
this.dataLen = dataLen;
|
||||
}
|
||||
|
||||
public String getRemoteHandle() {
|
||||
return this.remoteHandle;
|
||||
}
|
||||
|
||||
public Path getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
public int getDataLen() {
|
||||
return this.dataLen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FileWrittenEvent [remoteHandle=" + this.remoteHandle
|
||||
+ ", file=" + this.file
|
||||
+ ", dataLen=" + this.dataLen
|
||||
+ (this.cause == null ? "" : ", cause=" + this.cause)
|
||||
+ ", clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An event emitted when a path is moved.
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class PathMovedEvent extends ApacheMinaSftpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Path srcPath;
|
||||
|
||||
private final Path dstPath;
|
||||
|
||||
public PathMovedEvent(Object source, Path srcPath, Path dstPath, @Nullable Throwable thrown) {
|
||||
super(source, thrown);
|
||||
this.srcPath = srcPath;
|
||||
this.dstPath = dstPath;
|
||||
}
|
||||
|
||||
public Path getSrcPath() {
|
||||
return this.srcPath;
|
||||
}
|
||||
|
||||
public Path getDstPath() {
|
||||
return this.dstPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathMovedEvent [srcPath=" + this.srcPath
|
||||
+ ", dstPath=" + this.dstPath
|
||||
+ (this.cause == null ? "" : ", cause=" + this.cause)
|
||||
+ ", clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An event emitted when a file or directory is removed.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class PathRemovedEvent extends ApacheMinaSftpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Path path;
|
||||
|
||||
private final boolean isDirectory;
|
||||
|
||||
public PathRemovedEvent(Object source, Path path, boolean isDirectory, @Nullable Throwable thrown) {
|
||||
super(source, thrown);
|
||||
this.path = path;
|
||||
this.isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return this.isDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathRemovedEvent [path=" + this.path
|
||||
+ ", isDirectory=" + this.isDirectory
|
||||
+ (this.cause == null ? "" : ", cause=" + this.cause)
|
||||
+ ", clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
|
||||
/**
|
||||
* An event emitted when a session is closed.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class SessionClosedEvent extends ApacheMinaSftpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SessionClosedEvent(ServerSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SessionClosedEvent [clientAddress=" + getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* https://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.sftp.server;
|
||||
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
|
||||
/**
|
||||
* An event emitted when a session is opened.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class SessionOpenedEvent extends ApacheMinaSftpEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final int clientVersion;
|
||||
|
||||
public SessionOpenedEvent(ServerSession session, int version) {
|
||||
super(session);
|
||||
this.clientVersion = version;
|
||||
}
|
||||
|
||||
public int getClientVersion() {
|
||||
return this.clientVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SessionOpenedEvent [clientVersion=" + this.clientVersion + ", clientAddress="
|
||||
+ getSession().getClientAddress() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides classes related to SFTP servers.
|
||||
*/
|
||||
package org.springframework.integration.sftp.server;
|
||||
@@ -29,6 +29,7 @@ import org.junit.BeforeClass;
|
||||
import org.springframework.integration.file.remote.RemoteFileTestSupport;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.sftp.server.ApacheMinaSftpEventListener;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
@@ -46,6 +47,8 @@ public class SftpTestSupport extends RemoteFileTestSupport {
|
||||
|
||||
private static SshServer server;
|
||||
|
||||
private static ApacheMinaSftpEventListener eventListener = new ApacheMinaSftpEventListener();
|
||||
|
||||
@Override
|
||||
public String getTargetLocalDirectoryName() {
|
||||
return targetLocalDirectory.getAbsolutePath() + File.separator;
|
||||
@@ -62,7 +65,12 @@ public class SftpTestSupport extends RemoteFileTestSupport {
|
||||
server.setPasswordAuthenticator((username, password, session) -> true);
|
||||
server.setPort(0);
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("hostkey.ser").toPath()));
|
||||
server.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
|
||||
SftpSubsystemFactory sftpFactory = new SftpSubsystemFactory();
|
||||
eventListener.setApplicationEventPublisher((ev) -> {
|
||||
// no-op
|
||||
});
|
||||
sftpFactory.addSftpEventListener(eventListener);
|
||||
server.setSubsystemFactories(Collections.singletonList(sftpFactory));
|
||||
server.setFileSystemFactory(new VirtualFileSystemFactory(remoteTemporaryFolder.getRoot().toPath()));
|
||||
server.start();
|
||||
port = server.getPort();
|
||||
@@ -78,6 +86,10 @@ public class SftpTestSupport extends RemoteFileTestSupport {
|
||||
return new CachingSessionFactory<>(factory);
|
||||
}
|
||||
|
||||
public static ApacheMinaSftpEventListener eventListener() {
|
||||
return eventListener;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception {
|
||||
server.stop();
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -40,17 +41,28 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.remote.MessageSessionCallback;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.sftp.SftpTestSupport;
|
||||
import org.springframework.integration.sftp.server.ApacheMinaSftpEvent;
|
||||
import org.springframework.integration.sftp.server.DirectoryCreatedEvent;
|
||||
import org.springframework.integration.sftp.server.FileWrittenEvent;
|
||||
import org.springframework.integration.sftp.server.PathMovedEvent;
|
||||
import org.springframework.integration.sftp.server.PathRemovedEvent;
|
||||
import org.springframework.integration.sftp.server.SessionClosedEvent;
|
||||
import org.springframework.integration.sftp.server.SessionOpenedEvent;
|
||||
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
@@ -118,6 +130,9 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
@Autowired
|
||||
private Config config;
|
||||
|
||||
@Autowired
|
||||
private SftpRemoteFileTemplate template;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.config.targetLocalDirectoryName = getTargetLocalDirectoryName();
|
||||
@@ -352,6 +367,9 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
|
||||
@Test
|
||||
public void testInt3088MPutNotRecursive() throws Exception {
|
||||
resetSessionCache();
|
||||
this.config.events.clear();
|
||||
this.config.latch = new CountDownLatch(1);
|
||||
Session<?> session = sessionFactory.getSession();
|
||||
session.close();
|
||||
session = TestUtils.getPropertyValue(session, "targetSession", Session.class);
|
||||
@@ -373,8 +391,86 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
.isIn("sftpTarget/localSource1.txt", "sftpTarget/localSource2.txt");
|
||||
assertThat(out.getPayload().get(1))
|
||||
.isIn("sftpTarget/localSource1.txt", "sftpTarget/localSource2.txt");
|
||||
verify(channel).chmod(384, "sftpTarget/localSource1.txt"); // 384 = 600 octal
|
||||
verify(channel).chmod(384, "sftpTarget/localSource2.txt");
|
||||
verify(channel).chmod(0600, "sftpTarget/localSource1.txt");
|
||||
verify(channel).chmod(0600, "sftpTarget/localSource2.txt");
|
||||
resetSessionCache();
|
||||
assertThat(this.config.latch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(this.config.events).hasSize(6);
|
||||
assertThat(this.config.events.get(0)).isInstanceOf(SessionOpenedEvent.class);
|
||||
assertThat(this.config.events.get(1)).isInstanceOf(FileWrittenEvent.class);
|
||||
assertThat(((FileWrittenEvent) this.config.events.get(1)).getFile().toString())
|
||||
.matches("/sftpTarget/localSource(1|2).txt.writing");
|
||||
assertThat(this.config.events.get(2)).isInstanceOf(PathMovedEvent.class);
|
||||
assertThat(((PathMovedEvent) this.config.events.get(2)).getSrcPath().toString())
|
||||
.matches("/sftpTarget/localSource(1|2).txt.writing");
|
||||
assertThat(((PathMovedEvent) this.config.events.get(2)).getDstPath().toString())
|
||||
.matches("/sftpTarget/localSource(1|2).txt");
|
||||
assertThat(this.config.events.get(3)).isInstanceOf(FileWrittenEvent.class);
|
||||
assertThat(((FileWrittenEvent) this.config.events.get(3)).getFile().toString())
|
||||
.matches("/sftpTarget/localSource(1|2).txt.writing");
|
||||
assertThat(this.config.events.get(4)).isInstanceOf(PathMovedEvent.class);
|
||||
assertThat(((PathMovedEvent) this.config.events.get(4)).getSrcPath().toString())
|
||||
.matches("/sftpTarget/localSource(1|2).txt.writing");
|
||||
assertThat(((PathMovedEvent) this.config.events.get(4)).getDstPath().toString())
|
||||
.matches("/sftpTarget/localSource(1|2).txt");
|
||||
assertThat(this.config.events.get(5)).isInstanceOf(SessionClosedEvent.class);
|
||||
this.config.events.clear();
|
||||
this.config.latch = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allEvents() throws InterruptedException {
|
||||
resetSessionCache();
|
||||
this.config.events.clear();
|
||||
this.config.latch = new CountDownLatch(1);
|
||||
this.template.execute(session -> {
|
||||
assertThat(session.mkdir("/sftpTarget/allEventsDir")).isTrue();
|
||||
session.write(new ByteArrayInputStream("foo".getBytes()), "/sftpTarget/allEventsDir/file.txt");
|
||||
session.append(new ByteArrayInputStream("bar".getBytes()), "/sftpTarget/allEventsDir/file.txt");
|
||||
session.rename("/sftpTarget/allEventsDir/file.txt", "/sftpTarget/allEventsDir/file2.txt");
|
||||
assertThat(session.remove("/sftpTarget/allEventsDir/file2.txt")).isTrue();
|
||||
session.rename("/sftpTarget/allEventsDir", "/sftpTarget/allEventsDir2");
|
||||
session.rmdir("/sftpTarget/allEventsDir2");
|
||||
return null;
|
||||
});
|
||||
resetSessionCache();
|
||||
assertThat(this.config.latch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(this.config.events).hasSize(9);
|
||||
assertThat(this.config.events.get(0)).isInstanceOf(SessionOpenedEvent.class);
|
||||
assertThat(this.config.events.get(1)).isInstanceOf(DirectoryCreatedEvent.class);
|
||||
DirectoryCreatedEvent dce = (DirectoryCreatedEvent) this.config.events.get(1);
|
||||
assertThat(dce.getPath().toString()).isEqualTo("/sftpTarget/allEventsDir");
|
||||
assertThat(this.config.events.get(2)).isInstanceOf(FileWrittenEvent.class);
|
||||
FileWrittenEvent fwe = (FileWrittenEvent) this.config.events.get(2);
|
||||
assertThat(fwe.getFile().toString()).isEqualTo("/sftpTarget/allEventsDir/file.txt");
|
||||
assertThat(fwe.getDataLen()).isEqualTo(3);
|
||||
assertThat(this.config.events.get(3)).isInstanceOf(FileWrittenEvent.class);
|
||||
fwe = (FileWrittenEvent) this.config.events.get(3);
|
||||
assertThat(fwe.getFile().toString()).isEqualTo("/sftpTarget/allEventsDir/file.txt");
|
||||
assertThat(fwe.getDataLen()).isEqualTo(3);
|
||||
assertThat(this.config.events.get(4)).isInstanceOf(PathMovedEvent.class);
|
||||
PathMovedEvent pme = (PathMovedEvent) this.config.events.get(4);
|
||||
assertThat(pme.getSrcPath().toString()).isEqualTo("/sftpTarget/allEventsDir/file.txt");
|
||||
assertThat(pme.getDstPath().toString()).isEqualTo("/sftpTarget/allEventsDir/file2.txt");
|
||||
assertThat(this.config.events.get(5)).isInstanceOf(PathRemovedEvent.class);
|
||||
PathRemovedEvent pre = (PathRemovedEvent) this.config.events.get(5);
|
||||
assertThat(pre.getPath().toString()).isEqualTo("/sftpTarget/allEventsDir/file2.txt");
|
||||
assertThat(pre.isDirectory()).isFalse();
|
||||
assertThat(this.config.events.get(6)).isInstanceOf(PathMovedEvent.class);
|
||||
pme = (PathMovedEvent) this.config.events.get(6);
|
||||
assertThat(pme.getSrcPath().toString()).isEqualTo("/sftpTarget/allEventsDir");
|
||||
assertThat(pme.getDstPath().toString()).isEqualTo("/sftpTarget/allEventsDir2");
|
||||
assertThat(this.config.events.get(7)).isInstanceOf(PathRemovedEvent.class);
|
||||
pre = (PathRemovedEvent) this.config.events.get(7);
|
||||
assertThat(pre.getPath().toString()).isEqualTo("/sftpTarget/allEventsDir2");
|
||||
assertThat(pre.isDirectory()).isTrue();
|
||||
assertThat(this.config.events.get(8)).isInstanceOf(SessionClosedEvent.class);
|
||||
this.config.events.clear();
|
||||
this.config.latch = null;
|
||||
}
|
||||
|
||||
private void resetSessionCache() {
|
||||
((CachingSessionFactory<?>) this.sessionFactory).resetCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -488,17 +584,49 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
|
||||
public static class Config {
|
||||
|
||||
final List<ApacheMinaSftpEvent> events = new ArrayList<>();
|
||||
|
||||
private volatile String targetLocalDirectoryName;
|
||||
|
||||
private volatile CountDownLatch latch;
|
||||
|
||||
@Bean
|
||||
public SessionFactory<LsEntry> sftpSessionFactory() {
|
||||
public SessionFactory<LsEntry> sftpSessionFactory(ApplicationContext context) {
|
||||
SftpServerOutboundTests.eventListener().setApplicationEventPublisher(context);
|
||||
return SftpServerOutboundTests.sessionFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SftpRemoteFileTemplate template(SessionFactory<LsEntry> sf) {
|
||||
return new SftpRemoteFileTemplate(sf);
|
||||
}
|
||||
|
||||
public String getTargetLocalDirectoryName() {
|
||||
return this.targetLocalDirectoryName;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationEventListeningMessageProducer events() {
|
||||
ApplicationEventListeningMessageProducer producer = new ApplicationEventListeningMessageProducer();
|
||||
producer.setEventTypes(ApacheMinaSftpEvent.class);
|
||||
producer.setOutputChannel(eventChannel());
|
||||
return producer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel eventChannel() {
|
||||
return (msg, timeout) -> {
|
||||
if (this.latch != null) {
|
||||
if (this.events.size() > 0 || msg.getPayload() instanceof SessionOpenedEvent) {
|
||||
this.events.add((ApacheMinaSftpEvent) msg.getPayload());
|
||||
if (msg.getPayload() instanceof SessionClosedEvent) {
|
||||
this.latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -55,6 +55,51 @@ In the preceding example, all application context events that match one of the t
|
||||
If a downstream component throws an exception, a `MessagingException` that contains the failed message and exception is sent to the channel named 'eventErrorChannel'.
|
||||
If no `error-channel` is specified and the downstream channels are synchronous, the exception is propagated to the caller.
|
||||
|
||||
Using Java to configure the same adapter:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public ApplicationEventListeningMessageProducer eventsAdapter(
|
||||
MessageChannel eventChannel, MessageChannel eventErrorChannel) {
|
||||
|
||||
ApplicationEventListeningMessageProducer producer =
|
||||
new ApplicationEventListeningMessageProducer();
|
||||
producer.setEventTypes(example.FooEvent.class, example.BarEvent.class, java.util.Date.class);
|
||||
producer.setOutputChannel(eventChannel);
|
||||
producer.setErrorChannel(eventErrorChannel);
|
||||
return producer;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
With the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public ApplicationEventListeningMessageProducer eventsAdapter() {
|
||||
|
||||
ApplicationEventListeningMessageProducer producer =
|
||||
new ApplicationEventListeningMessageProducer();
|
||||
producer.setEventTypes(example.FooEvent.class, example.BarEvent.class, java.util.Date.class);
|
||||
return producer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow eventFlow(ApplicationEventListeningMessageProducer eventsAdapter,
|
||||
MessageChannel eventErrorChannel) {
|
||||
|
||||
return IntegrationFlows.from(eventsAdapter, e -> e.errorChannel(eventErrorChannel))
|
||||
.handle(...)
|
||||
...
|
||||
.get();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[appevent-outbound]]
|
||||
=== Sending Spring Application Events
|
||||
|
||||
@@ -72,7 +117,7 @@ For convenience, namespace support is provided to configure an `ApplicationEven
|
||||
----
|
||||
====
|
||||
|
||||
If you use a `PollableChannel` (such as a `Queue`), you can also provide `poller` as a child element of the `outbound-channel-adapter` element.
|
||||
If you use a `PollableChannel` (such as a `QueueChannel`), you can also provide a `poller` child element of the `outbound-channel-adapter` element.
|
||||
You can also optionally provide a `task-executor` reference for that poller.
|
||||
The following example demonstrates both:
|
||||
|
||||
@@ -96,3 +141,40 @@ If the payload of the message is an `ApplicationEvent`, it is passed as-is.
|
||||
Otherwise, the message itself is wrapped in a `MessagingEvent` instance.
|
||||
|
||||
Starting with version 4.2, you can configure the `ApplicationEventPublishingMessageHandler` (`<int-event:outbound-channel-adapter>`) with the `publish-payload` boolean attribute to publish to the application context `payload` as is, instead of wrapping it to a `MessagingEvent` instance.
|
||||
|
||||
To configure the adapter using Java configuration:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "eventChannel")
|
||||
public ApplicationEventPublishingMessageHandler eventHandler() {
|
||||
ApplicationEventPublishingMessageHandler handler =
|
||||
new ApplicationEventPublishingMessageHandler();
|
||||
handler.setPublishPayload(true);
|
||||
return handler;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
With the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public ApplicationEventPublishingMessageHandler eventHandler() {
|
||||
ApplicationEventPublishingMessageHandler handler =
|
||||
new ApplicationEventPublishingMessageHandler();
|
||||
handler.setPublishPayload(true);
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
// MessageChannel is "eventsFlow.input"
|
||||
public IntegrationFlow eventsOutFlow(ApplicationEventPublishingMessageHandler eventHandler) {
|
||||
return f -> f.handle(eventHandler);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1508,3 +1508,54 @@ When using XML configuration, the `<int-ftp:outbound-gateway/>` provides a `sess
|
||||
|
||||
NOTE: The `session-callback` is mutually exclusive with the `command` and `expression` attributes.
|
||||
When configuring with Java, different constructors are available in the https://docs.spring.io/spring-integration/api/org/springframework/integration/ftp/gateway/FtpOutboundGateway.html[`FtpOutboundGateway`] class.
|
||||
|
||||
[[ftp-server-events]]
|
||||
=== Apache Mina FTP Server Events
|
||||
|
||||
The `ApacheMinaFtplet`, added in version 5.2, listens for certain Apache Mina FTP server events and publishes them as `ApplicationEvent` s which can be received by any `ApplicationListener` bean, `@EventListener` bean method, or <<./event.adoc#appevent-inbound, Event Inbound Channel Adapter>>.
|
||||
|
||||
Currently supported events are:
|
||||
|
||||
* `SessionOpenedEvent` - a client session was opened
|
||||
* `DirectoryCreatedEvent` - a directory was created
|
||||
* `FileWrittenEvent` - a file was written to
|
||||
* `PathMovedEvent` - a file or directory was renamed
|
||||
* `PathRemovedEvent` - a file or directory was removed
|
||||
* `SessionClosedEvent` - the client has disconnected
|
||||
|
||||
Each of these is a subclass of `ApacheMinaFtpEvent`; you can configure a single listener to receive all of the event types.
|
||||
The `source` property of each event is a `FtpSession`, from which you can obtain information such as the client address; a convenient `getSession()` method is provided on the abstract event.
|
||||
|
||||
Events other than session open/close have another property `FtpRequest` which has properties such as the command and arguments.
|
||||
|
||||
To configure the server with the listener (which must be a Spring bean), add it to the server factory:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
FtpServerFactory serverFactory = new FtpServerFactory();
|
||||
...
|
||||
ListenerFactory factory = new ListenerFactory();
|
||||
...
|
||||
serverFactory.addListener("default", factory.createListener());
|
||||
serverFactory.setFtplets(new HashMap<>(Collections.singletonMap("springFtplet", apacheMinaFtpletBean)));
|
||||
server = serverFactory.createServer();
|
||||
server.start();
|
||||
----
|
||||
====
|
||||
|
||||
To consume these events using a Spring Integration event adapter:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public ApplicationEventListeningMessageProducer eventsAdapter() {
|
||||
ApplicationEventListeningMessageProducer producer =
|
||||
new ApplicationEventListeningMessageProducer();
|
||||
producer.setEventTypes(ApacheMinaFtpEvent.class);
|
||||
producer.setOutputChannel(eventChannel());
|
||||
return producer;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1387,3 +1387,49 @@ When using XML configuration, the `<int-sftp:outbound-gateway/>` provides a `ses
|
||||
|
||||
NOTE: The `session-callback` is mutually exclusive with the `command` and `expression` attributes.
|
||||
When configuring with Java, the `SftpOutboundGateway` class offers different constructors.
|
||||
|
||||
[[sftp-server-events]]
|
||||
=== Apache Mina SFTP Server Events
|
||||
|
||||
The `ApacheMinaSftpEventListener`, added in version 5.2, listens for certain Apache Mina SFTP server events and publishes them as `ApplicationEvent` s which can be received by any `ApplicationListener` bean, `@EventListener` bean method, or <<./event.adoc#appevent-inbound, Event Inbound Channel Adapter>>.
|
||||
|
||||
Currently supported events are:
|
||||
|
||||
* `SessionOpenedEvent` - a client session was opened
|
||||
* `DirectoryCreatedEvent` - a directory was created
|
||||
* `FileWrittenEvent` - a file was written to
|
||||
* `PathMovedEvent` - a file or directory was renamed
|
||||
* `PathRemovedEvent` - a file or directory was removed
|
||||
* `SessionClosedEvent` - the client has disconnected
|
||||
|
||||
Each of these is a subclass of `ApacheMinaSftpEvent`; you can configure a single listener to receive all of the event types.
|
||||
The `source` property of each event is a `ServerSession`, from which you can obtain information such as the client address; a convenient `getSession()` method is provided on the abstract event.
|
||||
|
||||
To configure the server with the listener (which must be a Spring bean), simply add it to the `SftpSubsystemFactory`:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
server = SshServer.setUpDefaultServer();
|
||||
...
|
||||
SftpSubsystemFactory sftpFactory = new SftpSubsystemFactory();
|
||||
sftpFactory.addSftpEventListener(apacheMinaSftpEventListenerBean);
|
||||
...
|
||||
----
|
||||
====
|
||||
|
||||
To consume these events using a Spring Integration event adapter:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public ApplicationEventListeningMessageProducer eventsAdapter() {
|
||||
ApplicationEventListeningMessageProducer producer =
|
||||
new ApplicationEventListeningMessageProducer();
|
||||
producer.setEventTypes(ApacheMinaSftpEvent.class);
|
||||
producer.setOutputChannel(eventChannel());
|
||||
return producer;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -49,6 +49,12 @@ See <<./scripting.adoc#scripting,Scripting Support>> for more information.
|
||||
The `FluxAggregatorMessageHandler` is now available for grouping and windowing messages logic based on the Project Reactor `Flux` operators.
|
||||
See <<./aggregator.adoc#flux-aggregator,Flux Aggregator>> for more information.
|
||||
|
||||
[[x5.2-sftp-events]]
|
||||
==== FTP/SFTP Event Publisher
|
||||
|
||||
The FTP and SFTP modules now provide an event listener for certain Apache Mina FTP/SFTP server events.
|
||||
See <<./ftp.adoc#ftp-server-events, Apache Mina FTP Server Events>> and <<./sftp.adoc#sftp-server-events, Apache Mina SFTP Server Events>> for more information.
|
||||
|
||||
[[x5.2-general]]
|
||||
=== General Changes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user