embulk-input-ftp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,267 @@
1
+ package org.embulk.input.ftp;
2
+
3
+ import java.util.concurrent.Future;
4
+ import java.util.concurrent.Callable;
5
+ import java.util.concurrent.CancellationException;
6
+ import java.util.concurrent.ExecutionException;
7
+ import java.util.concurrent.ExecutorService;
8
+ import java.io.IOException;
9
+ import java.io.EOFException;
10
+ import java.io.InterruptedIOException;
11
+ import java.nio.ByteBuffer;
12
+ import java.nio.channels.ReadableByteChannel;
13
+ import java.nio.channels.WritableByteChannel;
14
+ import com.google.common.base.Function;
15
+
16
+ public class BlockingTransfer
17
+ {
18
+ private final WriterChannel writerChannel;
19
+ private final ReaderChannel readerChannel;
20
+ private Future<?> transferCompletionFuture;
21
+
22
+ public static BlockingTransfer submit(ExecutorService executor,
23
+ Function<BlockingTransfer, Runnable> starterFactory)
24
+ {
25
+ BlockingTransfer transfer = new BlockingTransfer();
26
+ final Runnable starter = starterFactory.apply(transfer);
27
+ transfer.setTransferCompletionFuture(
28
+ executor.submit(new Callable<Void>() {
29
+ public Void call() throws Exception
30
+ {
31
+ starter.run();
32
+ return null;
33
+ }
34
+ })
35
+ );
36
+ return transfer;
37
+ }
38
+
39
+ private BlockingTransfer()
40
+ {
41
+ this.writerChannel = new WriterChannel();
42
+ this.readerChannel = new ReaderChannel();
43
+ }
44
+
45
+ private void setTransferCompletionFuture(Future<?> future)
46
+ {
47
+ this.transferCompletionFuture = future;
48
+ }
49
+
50
+ public ReadableByteChannel getReaderChannel()
51
+ {
52
+ return readerChannel;
53
+ }
54
+
55
+ public WritableByteChannel getWriterChannel()
56
+ {
57
+ return writerChannel;
58
+ }
59
+
60
+ public void transferFailed(Throwable exception)
61
+ {
62
+ readerChannel.overwriteException(exception);
63
+ }
64
+
65
+ void waitForTransferCompletion() throws IOException
66
+ {
67
+ Future<?> f = transferCompletionFuture;
68
+ if (f != null) {
69
+ try {
70
+ f.get();
71
+ } catch (CancellationException ex) {
72
+ throw new InterruptedIOException();
73
+ } catch (InterruptedException ex) {
74
+ throw new InterruptedIOException();
75
+ } catch (ExecutionException ex) {
76
+ // transfer failed
77
+ Throwable e = ex.getCause();
78
+ if (e instanceof IOException) {
79
+ throw (IOException) e;
80
+ } else if (e instanceof RuntimeException) {
81
+ throw (RuntimeException) e;
82
+ } else if (e instanceof Error) {
83
+ throw (Error) e;
84
+ } else {
85
+ throw new IOException(e);
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ public class WriterChannel implements WritableByteChannel
92
+ {
93
+ public int write(ByteBuffer src) throws IOException
94
+ {
95
+ int sz = src.remaining();
96
+ if (sz <= 0) {
97
+ return sz;
98
+ }
99
+
100
+ synchronized(readerChannel) {
101
+ if (!readerChannel.waitForWritable()) {
102
+ return -1;
103
+ }
104
+
105
+ readerChannel.setBuffer(src);
106
+
107
+ if (!readerChannel.waitForWritable()) { // wait for complete processing src
108
+ return -1;
109
+ }
110
+ }
111
+
112
+ return sz - src.remaining();
113
+ }
114
+
115
+ public boolean isOpen()
116
+ {
117
+ return readerChannel.isOpen();
118
+ }
119
+
120
+ public void close() throws IOException
121
+ {
122
+ readerChannel.closePeer();
123
+ waitForTransferCompletion();
124
+ }
125
+ }
126
+
127
+ private static int transferByteBuffer(ByteBuffer src, ByteBuffer dst)
128
+ {
129
+ int pos = dst.position();
130
+
131
+ int srcrem = src.remaining();
132
+ int dstrem = dst.remaining();
133
+ if (dstrem < srcrem) {
134
+ int lim = src.limit();
135
+ try {
136
+ src.limit(src.position() + dstrem);
137
+ dst.put(src);
138
+ } finally {
139
+ src.limit(lim);
140
+ }
141
+ } else {
142
+ dst.put(src);
143
+ }
144
+
145
+ return dst.position() - pos;
146
+ }
147
+
148
+ public class ReaderChannel implements ReadableByteChannel
149
+ {
150
+ private ByteBuffer buffer;
151
+ private Throwable exception;
152
+
153
+ public synchronized int read(ByteBuffer dst) throws IOException
154
+ {
155
+ if (!waitForReadable()) {
156
+ return -1;
157
+ }
158
+
159
+ int len = transferByteBuffer(buffer, dst);
160
+ if (!buffer.hasRemaining()) {
161
+ setBuffer(null);
162
+ notifyAll();
163
+ }
164
+
165
+ return len;
166
+ }
167
+
168
+ public synchronized boolean isOpen()
169
+ {
170
+ return exception == null;
171
+ }
172
+
173
+ public void close() throws IOException
174
+ {
175
+ setException(new EOFException("reader closed channel"));
176
+ }
177
+
178
+ private void setBuffer(ByteBuffer buffer)
179
+ {
180
+ this.buffer = buffer;
181
+ notifyAll();
182
+ }
183
+
184
+ private synchronized boolean waitForWritable() throws IOException
185
+ {
186
+ while (buffer != null) {
187
+ if (exception != null) {
188
+ if (exception instanceof EOFException) {
189
+ return false;
190
+ }
191
+ throwException();
192
+ }
193
+
194
+ try {
195
+ wait();
196
+ } catch (InterruptedException ex) {
197
+ // TODO throws ClosedByInterruptException or InterruptedIOException?
198
+ }
199
+ }
200
+
201
+ return true;
202
+ }
203
+
204
+ private boolean waitForReadable() throws IOException
205
+ {
206
+ while(buffer == null) {
207
+ if (exception != null) {
208
+ if (exception instanceof EOFException) {
209
+ return false;
210
+ }
211
+ throwException();
212
+ }
213
+
214
+ try {
215
+ wait();
216
+ } catch (InterruptedException ex) {
217
+ // TODO throws ClosedByInterruptException or InterruptedIOException?
218
+ }
219
+ }
220
+
221
+ return true;
222
+ }
223
+
224
+ public synchronized void closePeer() throws IOException
225
+ {
226
+ waitForWritable();
227
+ if( exception != null && !(exception instanceof EOFException)) {
228
+ throwException();
229
+ }
230
+ setException(new EOFException("writer closed channel"));
231
+ }
232
+
233
+ public synchronized void setException(Throwable exception)
234
+ {
235
+ if (this.exception == null) {
236
+ this.exception = exception;
237
+ }
238
+ notifyAll();
239
+ }
240
+
241
+ public synchronized void overwriteException(Throwable exception)
242
+ {
243
+ this.exception = exception;
244
+ notifyAll();
245
+ }
246
+
247
+ public boolean hasException()
248
+ {
249
+ return exception != null;
250
+ }
251
+
252
+ public void throwException() throws IOException
253
+ {
254
+ Throwable ex = exception;
255
+ if (ex instanceof IOException) {
256
+ throw (IOException) ex;
257
+ } else if (ex instanceof RuntimeException) {
258
+ throw (RuntimeException) ex;
259
+ } else if (ex instanceof Error) {
260
+ throw (Error) ex;
261
+ } else {
262
+ throw new IOException(ex);
263
+ }
264
+ }
265
+ }
266
+ }
267
+
@@ -0,0 +1,131 @@
1
+ // TODO copied from S3FileInputPlugin. This should be moved to org.embulk.
2
+ package org.embulk.input.ftp;
3
+
4
+ import java.util.concurrent.Callable;
5
+ import java.util.concurrent.ExecutionException;
6
+
7
+ public class RetryExecutor
8
+ {
9
+ public static RetryExecutor retryExecutor()
10
+ {
11
+ // TODO default configuration
12
+ return new RetryExecutor(3, 500, 30*60*1000);
13
+ }
14
+
15
+ public static class RetryGiveupException
16
+ extends ExecutionException
17
+ {
18
+ public RetryGiveupException(String message, Exception cause)
19
+ {
20
+ super(cause);
21
+ }
22
+
23
+ public RetryGiveupException(Exception cause)
24
+ {
25
+ super(cause);
26
+ }
27
+
28
+ public Exception getCause()
29
+ {
30
+ return (Exception) super.getCause();
31
+ }
32
+ }
33
+
34
+ public static interface Retryable<T>
35
+ extends Callable<T>
36
+ {
37
+ public T call()
38
+ throws Exception;
39
+
40
+ public boolean isRetryableException(Exception exception);
41
+
42
+ public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait)
43
+ throws RetryGiveupException;
44
+
45
+ public void onGiveup(Exception firstException, Exception lastException)
46
+ throws RetryGiveupException;
47
+ }
48
+
49
+ private final int retryLimit;
50
+ private final int initialRetryWait;
51
+ private final int maxRetryWait;
52
+
53
+ private RetryExecutor(int retryLimit, int initialRetryWait, int maxRetryWait)
54
+ {
55
+ this.retryLimit = retryLimit;
56
+ this.initialRetryWait = initialRetryWait;
57
+ this.maxRetryWait = maxRetryWait;
58
+ }
59
+
60
+ public RetryExecutor withRetryLimit(int count)
61
+ {
62
+ return new RetryExecutor(count, initialRetryWait, maxRetryWait);
63
+ }
64
+
65
+ public RetryExecutor withInitialRetryWait(int msec)
66
+ {
67
+ return new RetryExecutor(retryLimit, msec, maxRetryWait);
68
+ }
69
+
70
+ public RetryExecutor withMaxRetryWait(int msec)
71
+ {
72
+ return new RetryExecutor(retryLimit, initialRetryWait, msec);
73
+ }
74
+
75
+ public <T> T runInterruptible(Retryable<T> op)
76
+ throws InterruptedException, RetryGiveupException
77
+ {
78
+ return run(op, true);
79
+ }
80
+
81
+ public <T> T run(Retryable<T> op)
82
+ throws RetryGiveupException
83
+ {
84
+ try {
85
+ return run(op, false);
86
+ } catch (InterruptedException ex) {
87
+ throw new RetryGiveupException("Unexpected interruption", ex);
88
+ }
89
+ }
90
+
91
+ private <T> T run(Retryable<T> op, boolean interruptible)
92
+ throws InterruptedException, RetryGiveupException
93
+ {
94
+ int retryWait = initialRetryWait;
95
+ int retryCount = 0;
96
+
97
+ Exception firstException = null;
98
+
99
+ while(true) {
100
+ try {
101
+ return op.call();
102
+ } catch (Exception exception) {
103
+ if (firstException == null) {
104
+ firstException = exception;
105
+ }
106
+ if (!op.isRetryableException(exception) || retryCount >= retryLimit) {
107
+ op.onGiveup(firstException, exception);
108
+ throw new RetryGiveupException(firstException);
109
+ }
110
+
111
+ retryCount++;
112
+ op.onRetry(exception, retryCount, retryLimit, retryWait);
113
+
114
+ try {
115
+ Thread.sleep(retryWait);
116
+ } catch (InterruptedException ex) {
117
+ if (interruptible) {
118
+ throw ex;
119
+ }
120
+ }
121
+
122
+ // exponential back-off with hard limit
123
+ retryWait *= 2;
124
+ if (retryWait > maxRetryWait) {
125
+ retryWait = maxRetryWait;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+
@@ -0,0 +1,129 @@
1
+ // TODO copied from S3FileInputPlugin. This should be moved to org.embulk.
2
+ package org.embulk.input.ftp;
3
+
4
+ import java.io.InputStream;
5
+ import java.io.IOException;
6
+
7
+ public class RetryableInputStream
8
+ extends InputStream
9
+ {
10
+ public interface Opener
11
+ {
12
+ public InputStream open(long offset, Exception exception) throws IOException;
13
+ }
14
+
15
+ private final Opener opener;
16
+ protected InputStream in;
17
+ private long offset;
18
+ private long markedOffset;
19
+
20
+ public RetryableInputStream(InputStream initialInputStream, Opener reopener)
21
+ {
22
+ this.opener = reopener;
23
+ this.in = initialInputStream;
24
+ this.offset = 0L;
25
+ this.markedOffset = 0L;
26
+ }
27
+
28
+ public RetryableInputStream(Opener opener) throws IOException
29
+ {
30
+ this(opener.open(0, null), opener);
31
+ }
32
+
33
+ private void reopen(Exception exception) throws IOException
34
+ {
35
+ if (in != null) {
36
+ in.close();
37
+ in = null;
38
+ }
39
+ in = opener.open(offset, exception);
40
+ }
41
+
42
+ @Override
43
+ public int read() throws IOException
44
+ {
45
+ while (true) {
46
+ try {
47
+ int v = in.read();
48
+ offset += 1;
49
+ return v;
50
+ } catch (IOException | RuntimeException ex) {
51
+ reopen(ex);
52
+ }
53
+ }
54
+ }
55
+
56
+ @Override
57
+ public int read(byte[] b) throws IOException
58
+ {
59
+ while (true) {
60
+ try {
61
+ int r = in.read(b);
62
+ offset += r;
63
+ return r;
64
+ } catch (IOException | RuntimeException ex) {
65
+ reopen(ex);
66
+ }
67
+ }
68
+ }
69
+
70
+ @Override
71
+ public int read(byte[] b, int off, int len) throws IOException
72
+ {
73
+ while (true) {
74
+ try {
75
+ int r = in.read(b, off, len);
76
+ offset += r;
77
+ return r;
78
+ } catch (IOException | RuntimeException ex) {
79
+ reopen(ex);
80
+ }
81
+ }
82
+ }
83
+
84
+ @Override
85
+ public long skip(long n) throws IOException
86
+ {
87
+ while (true) {
88
+ try {
89
+ long r = in.skip(n);
90
+ offset += r;
91
+ return r;
92
+ } catch (IOException | RuntimeException ex) {
93
+ reopen(ex);
94
+ }
95
+ }
96
+ }
97
+
98
+ @Override
99
+ public int available() throws IOException
100
+ {
101
+ return in.available();
102
+ }
103
+
104
+ @Override
105
+ public void close() throws IOException
106
+ {
107
+ in.close();
108
+ }
109
+
110
+ @Override
111
+ public void mark(int readlimit)
112
+ {
113
+ in.mark(readlimit);
114
+ markedOffset = offset;
115
+ }
116
+
117
+ @Override
118
+ public void reset() throws IOException
119
+ {
120
+ in.reset();
121
+ offset = markedOffset;
122
+ }
123
+
124
+ @Override
125
+ public boolean markSupported()
126
+ {
127
+ return in.markSupported();
128
+ }
129
+ }