ret = new ArrayList<>();
81 |
82 | for (String f : listing.split("\n")) {
83 | if (f.endsWith("\r")) {
84 | f = f.substring(0, f.length() - 1);
85 | }
86 | FtpFile file = FtpFile.from(f.toString());
87 |
88 | if (file == null) {
89 | continue;
90 | }
91 | if (file.name.equals(".") || file.name.equals("..")) {
92 | continue;
93 | }
94 |
95 | ret.add(file);
96 | }
97 | return ret;
98 | }
99 |
100 | @Override
101 | public String toString() {
102 | return "FtpFile [type=" + type + ", name=" + name + ", size=" + size + ", date=" + date + "]";
103 | }
104 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/bckfnn/ftp/Progress.java:
--------------------------------------------------------------------------------
1 | package io.github.bckfnn.ftp;
2 |
3 | public class Progress {
4 | private FtpClient client;
5 | private int count;
6 |
7 |
8 | public Progress(FtpClient client, int count) {
9 | super();
10 | this.client = client;
11 | this.count = count;
12 | }
13 |
14 | public FtpClient client() {
15 | return client;
16 | }
17 |
18 | public int count() {
19 | return count;
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/io/github/bckfnn/ftp/ProgressPump.java:
--------------------------------------------------------------------------------
1 | package io.github.bckfnn.ftp;
2 |
3 | import io.vertx.core.Handler;
4 | import io.vertx.core.buffer.Buffer;
5 | import io.vertx.core.streams.Pump;
6 | import io.vertx.core.streams.ReadStream;
7 | import io.vertx.core.streams.WriteStream;
8 |
9 | /**
10 | * Pumps data from a {@link io.vertx.core.streams.ReadStream} to a {@link io.vertx.core.streams.WriteStream} and performs flow control where necessary to
11 | * prevent the write stream buffer from getting overfull.
12 | * Instances of this class read bytes from a {@link io.vertx.core.streams.ReadStream} and write them to a {@link io.vertx.core.streams.WriteStream}. If data
13 | * can be read faster than it can be written this could result in the write queue of the {@link io.vertx.core.streams.WriteStream} growing
14 | * without bound, eventually causing it to exhaust all available RAM.
15 | * To prevent this, after each write, instances of this class check whether the write queue of the {@link
16 | * io.vertx.core.streams.WriteStream} is full, and if so, the {@link io.vertx.core.streams.ReadStream} is paused, and a {@code drainHandler} is set on the
17 | * {@link io.vertx.core.streams.WriteStream}. When the {@link io.vertx.core.streams.WriteStream} has processed half of its backlog, the {@code drainHandler} will be
18 | * called, which results in the pump resuming the {@link io.vertx.core.streams.ReadStream}.
19 | * This class can be used to pump from any {@link io.vertx.core.streams.ReadStream} to any {@link io.vertx.core.streams.WriteStream},
20 | * e.g. from an {@link io.vertx.core.http.HttpServerRequest} to an {@link io.vertx.core.file.AsyncFile},
21 | * or from {@link io.vertx.core.net.NetSocket} to a {@link io.vertx.core.http.WebSocket}.
22 | *
23 | * Instances of this class are not thread-safe.
24 | *
25 | * @author Tim Fox
26 | */
27 | public class ProgressPump implements Pump {
28 |
29 | private final ReadStream readStream;
30 | private final WriteStream writeStream;
31 | private final Handler dataHandler;
32 | private final Handler drainHandler;
33 | private int pumped;
34 | private Handler progress;
35 |
36 | /**
37 | * Create a new {@code Pump} with the given {@code ReadStream} and {@code WriteStream}. Set the write queue max size
38 | * of the write stream to {@code maxWriteQueueSize}
39 | */
40 | ProgressPump(ReadStream rs, WriteStream ws, Handler progress, int maxWriteQueueSize) {
41 | this(rs, ws, progress);
42 | this.writeStream.setWriteQueueMaxSize(maxWriteQueueSize);
43 | }
44 |
45 | ProgressPump(ReadStream rs, WriteStream ws, Handler progress) {
46 | this.readStream = rs;
47 | this.writeStream = ws;
48 | this.progress = progress;
49 | drainHandler = v-> readStream.resume();
50 | dataHandler = data -> {
51 | writeStream.write(data);
52 | incPumped(data);
53 | if (writeStream.writeQueueFull()) {
54 | readStream.pause();
55 | writeStream.drainHandler(drainHandler);
56 | }
57 | };
58 | }
59 |
60 | /**
61 | * Set the write queue max size to {@code maxSize}
62 | */
63 | @Override
64 | public ProgressPump setWriteQueueMaxSize(int maxSize) {
65 | writeStream.setWriteQueueMaxSize(maxSize);
66 | return this;
67 | }
68 |
69 | /**
70 | * Start the Pump. The Pump can be started and stopped multiple times.
71 | */
72 | @Override
73 | public ProgressPump start() {
74 | readStream.handler(dataHandler);
75 | return this;
76 | }
77 |
78 | /**
79 | * Stop the Pump. The Pump can be started and stopped multiple times.
80 | */
81 | @Override
82 | public ProgressPump stop() {
83 | writeStream.drainHandler(null);
84 | readStream.handler(null);
85 | return this;
86 | }
87 |
88 | /**
89 | * Return the total number of elements pumped by this pump.
90 | */
91 | @Override
92 | public synchronized int numberPumped() {
93 | return pumped;
94 | }
95 |
96 | // Note we synchronize as numberPumped can be called from a different thread however incPumped will always
97 | // be called from the same thread so we benefit from bias locked optimisation which should give a very low
98 | // overhead
99 | private synchronized void incPumped(Buffer buf) {
100 | pumped += buf.length();
101 | progress.handle(pumped);
102 | }
103 |
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/io/github/bckfnn/ftp/Response.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Finn Bock
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.github.bckfnn.ftp;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | import io.vertx.core.AsyncResult;
22 | import io.vertx.core.Future;
23 | import io.vertx.core.Handler;
24 |
25 | public class Response {
26 | protected String code;
27 | protected List messages = new ArrayList<>();
28 | Handler> handler;
29 |
30 | public Response(Handler> handler) {
31 | this.handler = handler;
32 | }
33 |
34 | public void setCode(String code) {
35 | this.code = code;
36 | }
37 |
38 | public String getCode() {
39 | return code;
40 | }
41 |
42 | public void addMessage(String message) {
43 | this.messages.add(message);
44 | }
45 |
46 | public boolean codeIs(String value) {
47 | return code.equals(value);
48 | }
49 |
50 | public void fail(String msg) {
51 | handler.handle(Future.failedFuture(new RuntimeException(msg)));
52 | }
53 |
54 | public void fail() {
55 | handler.handle(Future.failedFuture(new RuntimeException(code + " " + messages.get(0))));
56 | }
57 |
58 | public void succes() {
59 | handler.handle(Future.succeededFuture(this));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/io/github/bckfnn/ftp/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Finn Bock
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * A simple ftp client for vertx.
19 | */
20 | package io.github.bckfnn.ftp;
--------------------------------------------------------------------------------
/src/test/java/io/github/bckfnn/ftp/FtpClientTele2Test.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Finn Bock
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.github.bckfnn.ftp;
17 |
18 | import java.util.UUID;
19 | import java.util.function.BiConsumer;
20 |
21 | import org.junit.AfterClass;
22 | import org.junit.BeforeClass;
23 | import org.junit.Test;
24 |
25 | import io.vertx.core.Vertx;
26 | import io.vertx.core.file.OpenOptions;
27 |
28 | public class FtpClientTele2Test {
29 | static Vertx vertx;
30 |
31 | @BeforeClass
32 | public static void setup() {
33 | vertx = Vertx.vertx();
34 | }
35 |
36 | @AfterClass
37 | public static void teardown() {
38 | vertx.close();
39 | }
40 |
41 | @Test
42 | public void testLogin() {
43 | login("speedtest.tele2.net", "anonymous", "passwd", (client, latch) -> {
44 | latch.countDown();
45 | });
46 | }
47 |
48 | @Test
49 | public void testList() {
50 | login("speedtest.tele2.net", "anonymous", "passwd", (client, latch) -> {
51 | client.list(list -> {
52 | if (latch.failed(list)) {
53 | return;
54 | }
55 | System.out.println("list " + list.result());
56 | latch.countDown();
57 | });
58 | });
59 | }
60 |
61 |
62 | @Test
63 | public void testListParsed() {
64 | login("speedtest.tele2.net", "anonymous", "passwd", (client, latch) -> {
65 | client.list(list -> {
66 | if (latch.failed(list)) {
67 | return;
68 | }
69 | for (FtpFile f : FtpFile.listing(list.result().toString())) {
70 | System.out.println(f);
71 | }
72 |
73 | latch.countDown();
74 | });
75 | });
76 | }
77 |
78 | @Test
79 | public void testRetr() {
80 | login("speedtest.tele2.net", "anonymous", "passwd", (client, latch) -> {
81 | vertx.fileSystem().open("target/tmp.zip", new OpenOptions().setWrite(true).setTruncateExisting(true), arfile -> {
82 | if (latch.failed(arfile)) {
83 | return;
84 | }
85 | client.retr("512KB.zip", arfile.result(), progress -> {}, retr -> {
86 | if (latch.failed(retr)) {
87 | return;
88 | }
89 | arfile.result().close(close -> {
90 | if (latch.failed(close)) {
91 | return;
92 | }
93 | latch.countDown();
94 | });
95 | });
96 |
97 | });
98 | });
99 | }
100 |
101 |
102 | @Test
103 | public void testStor() {
104 | login("speedtest.tele2.net", "anonymous", "passwd", (client, latch) -> {
105 | vertx.fileSystem().open("LICENSE-2.0.txt", new OpenOptions().setRead(true), arfile -> {
106 | if (latch.failed(arfile)) {
107 | return;
108 | }
109 | client.stor("upload/LICENSE-2.0.txt-" + UUID.randomUUID().toString(), arfile.result(), progress -> {}, retr -> {
110 | if (latch.failed(retr)) {
111 | return;
112 | }
113 | arfile.result().close(close -> {
114 | if (latch.failed(close)) {
115 | return;
116 | }
117 | latch.countDown();
118 | });
119 | });
120 |
121 | });
122 | });
123 | }
124 |
125 | private void login(String host, String user, String pass, BiConsumer> handler) {
126 | HandlerLatch latch = new HandlerLatch<>();
127 |
128 | FtpClient client = new FtpClient(vertx, host, 21);
129 | client.connect(connect -> {
130 | if (latch.failed(connect)) {
131 | return;
132 | }
133 | client.login(user, pass, login -> {
134 | if (latch.failed(login)) {
135 | return;
136 | }
137 | handler.accept(client, latch);
138 | });
139 | });
140 | latch.await();
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/test/java/io/github/bckfnn/ftp/HandlerLatch.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Finn Bock
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.github.bckfnn.ftp;
17 |
18 | import java.util.concurrent.CountDownLatch;
19 |
20 | import org.junit.Assert;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 |
24 | import io.vertx.core.AsyncResult;
25 | import io.vertx.core.AsyncResultHandler;
26 |
27 | public class HandlerLatch implements AsyncResultHandler {
28 | static Logger log = LoggerFactory.getLogger(HandlerLatch.class);
29 |
30 | private CountDownLatch latch = new CountDownLatch(1);
31 | private Throwable error;
32 |
33 | @Override
34 | public void handle(AsyncResult event) {
35 | System.out.println(event.succeeded() + " " + event.result() + " " + event.cause());
36 | if (event.failed()) {
37 | log.error("caugth", event.cause());
38 | latch.countDown();
39 | }
40 | }
41 |
42 | public void countDown() {
43 | latch.countDown();
44 | log.debug("countDown");
45 | }
46 |
47 | public Void fail(Throwable error) {
48 | this.error = error;
49 | latch.countDown();
50 | return null;
51 | }
52 |
53 | public boolean failed(AsyncResult asyncResult) {
54 | if (asyncResult.failed()) {
55 | this.error = asyncResult.cause();
56 | latch.countDown();
57 | return true;
58 | }
59 | return false;
60 | }
61 |
62 | public void await() {
63 | try {
64 | latch.await();
65 | } catch (InterruptedException e) {
66 | e.printStackTrace();
67 | }
68 | log.debug("awaited");
69 | if (error != null) {
70 | Assert.assertTrue(error.toString(), false);
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------