embulk-output-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,456 @@
1
+ package org.embulk.output.ftp;
2
+
3
+ import com.google.common.base.Optional;
4
+ import com.google.common.base.Throwables;
5
+ import it.sauronsoftware.ftp4j.FTPAbortedException;
6
+ import it.sauronsoftware.ftp4j.FTPClient;
7
+ import it.sauronsoftware.ftp4j.FTPCommunicationListener;
8
+ import it.sauronsoftware.ftp4j.FTPConnector;
9
+ import it.sauronsoftware.ftp4j.FTPDataTransferException;
10
+ import it.sauronsoftware.ftp4j.FTPDataTransferListener;
11
+ import it.sauronsoftware.ftp4j.FTPException;
12
+ import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
13
+ import org.embulk.config.Config;
14
+ import org.embulk.config.ConfigDefault;
15
+ import org.embulk.config.ConfigDiff;
16
+ import org.embulk.config.ConfigException;
17
+ import org.embulk.config.ConfigSource;
18
+ import org.embulk.config.Task;
19
+ import org.embulk.config.TaskReport;
20
+ import org.embulk.config.TaskSource;
21
+ import org.embulk.output.ftp.SSLPlugins.SSLPluginConfig;
22
+ import org.embulk.spi.Buffer;
23
+ import org.embulk.spi.Exec;
24
+ import org.embulk.spi.FileOutputPlugin;
25
+ import org.embulk.spi.TransactionalFileOutput;
26
+ import org.embulk.spi.util.RetryExecutor.RetryGiveupException;
27
+ import org.embulk.spi.util.RetryExecutor.Retryable;
28
+ import org.slf4j.Logger;
29
+ import static org.embulk.spi.util.RetryExecutor.retryExecutor;
30
+
31
+ import java.io.BufferedInputStream;
32
+ import java.io.BufferedOutputStream;
33
+ import java.io.File;
34
+ import java.io.FileInputStream;
35
+ import java.io.FileNotFoundException;
36
+ import java.io.FileOutputStream;
37
+ import java.io.IOException;
38
+ import java.net.URISyntaxException;
39
+ import java.util.List;
40
+
41
+ public class FtpFileOutputPlugin implements FileOutputPlugin
42
+ {
43
+ public interface PluginTask extends Task, SSLPlugins.SSLPluginTask
44
+ {
45
+ @Config("host")
46
+ String getHost();
47
+
48
+ @Config("port")
49
+ @ConfigDefault("null")
50
+ Optional<Integer> getPort();
51
+ void setPort(Optional<Integer> port);
52
+
53
+ @Config("user")
54
+ @ConfigDefault("null")
55
+ Optional<String> getUser();
56
+
57
+ @Config("password")
58
+ @ConfigDefault("null")
59
+ Optional<String> getPassword();
60
+
61
+ @Config("passive_mode")
62
+ @ConfigDefault("true")
63
+ boolean getPassiveMode();
64
+
65
+ @Config("ascii_mode")
66
+ @ConfigDefault("false")
67
+ boolean getAsciiMode();
68
+
69
+ @Config("ssl")
70
+ @ConfigDefault("false")
71
+ boolean getSsl();
72
+
73
+ SSLPluginConfig getSSLConfig();
74
+ void setSSLConfig(SSLPluginConfig config);
75
+
76
+ @Config("path_prefix")
77
+ String getPathPrefix();
78
+
79
+ @Config("file_ext")
80
+ String getFileNameExtension();
81
+
82
+ @Config("sequence_format")
83
+ @ConfigDefault("\"%03d.%02d\"")
84
+ String getSequenceFormat();
85
+
86
+ @Config("max_connection_retry")
87
+ @ConfigDefault("10") // 10 times retry to connect FTP server if failed.
88
+ int getMaxConnectionRetry();
89
+ }
90
+
91
+ private static final Logger log = Exec.getLogger(FtpFileOutputPlugin.class);
92
+ private static final long TRANSFER_NOTICE_BYTES = 100 * 1024 * 1024;
93
+
94
+ @Override
95
+ public ConfigDiff transaction(ConfigSource config, int taskCount, FileOutputPlugin.Control control)
96
+ {
97
+ PluginTask task = config.loadConfig(PluginTask.class);
98
+ task.setSSLConfig(SSLPlugins.configure(task));
99
+
100
+ // try to check if plugin could connect to FTP server
101
+ FTPClient client = null;
102
+ try {
103
+ client = newFTPClient(log, task);
104
+ }
105
+ catch (Exception ex) {
106
+ throw new ConfigException(ex);
107
+ }
108
+ finally {
109
+ disconnectClient(client);
110
+ }
111
+
112
+ return resume(task.dump(), taskCount, control);
113
+ }
114
+
115
+ @Override
116
+ public ConfigDiff resume(TaskSource taskSource, int taskCount, FileOutputPlugin.Control control)
117
+ {
118
+ control.run(taskSource);
119
+
120
+ return Exec.newConfigDiff();
121
+ }
122
+
123
+ @Override
124
+ public void cleanup(TaskSource taskSource, int taskCount, List<TaskReport> successTaskReports)
125
+ {
126
+ }
127
+
128
+ @Override
129
+ public TransactionalFileOutput open(TaskSource taskSource, final int taskIndex)
130
+ {
131
+ final PluginTask task = taskSource.loadTask(PluginTask.class);
132
+
133
+ FTPClient client = newFTPClient(log, task);
134
+ return new FtpFileOutput(client, task, taskIndex);
135
+ }
136
+
137
+ public static class FtpFileOutput implements TransactionalFileOutput
138
+ {
139
+ private final FTPClient client;
140
+ private final String pathPrefix;
141
+ private final String sequenceFormat;
142
+ private final String pathSuffix;
143
+ private final int maxConnectionRetry;
144
+ private BufferedOutputStream output = null;
145
+ private int fileIndex;
146
+ private File file;
147
+ private String filePath;
148
+ private int taskIndex;
149
+
150
+ public FtpFileOutput(FTPClient client, PluginTask task, int taskIndex)
151
+ {
152
+ this.client = client;
153
+ this.taskIndex = taskIndex;
154
+ this.pathPrefix = task.getPathPrefix();
155
+ this.sequenceFormat = task.getSequenceFormat();
156
+ this.pathSuffix = task.getFileNameExtension();
157
+ this.maxConnectionRetry = task.getMaxConnectionRetry();
158
+ }
159
+
160
+ @Override
161
+ public void nextFile()
162
+ {
163
+ closeFile();
164
+
165
+ try {
166
+ String suffix = pathSuffix;
167
+ if (!suffix.startsWith(".")) {
168
+ suffix = "." + suffix;
169
+ }
170
+ filePath = pathPrefix + String.format(sequenceFormat, taskIndex, fileIndex) + suffix;
171
+ file = File.createTempFile(filePath, ".tmp");
172
+ log.info("Writing local file {}", file.getAbsolutePath());
173
+ output = new BufferedOutputStream(new FileOutputStream(file));
174
+ }
175
+ catch (IOException ex) {
176
+ throw Throwables.propagate(ex);
177
+ }
178
+ }
179
+
180
+ private void closeFile()
181
+ {
182
+ if (output != null) {
183
+ try {
184
+ output.close();
185
+ fileIndex++;
186
+ }
187
+ catch (IOException ex) {
188
+ throw Throwables.propagate(ex);
189
+ }
190
+ }
191
+ }
192
+
193
+ @Override
194
+ public void add(Buffer buffer)
195
+ {
196
+ try {
197
+ output.write(buffer.array(), buffer.offset(), buffer.limit());
198
+ }
199
+ catch (IOException ex) {
200
+ throw Throwables.propagate(ex);
201
+ }
202
+ finally {
203
+ buffer.release();
204
+ }
205
+ }
206
+
207
+ @Override
208
+ public void finish()
209
+ {
210
+ close();
211
+ uploadFile();
212
+ disconnectClient(client);
213
+ }
214
+
215
+ private Void uploadFile()
216
+ {
217
+ if (filePath != null) {
218
+ try {
219
+ return retryExecutor()
220
+ .withRetryLimit(maxConnectionRetry)
221
+ .withInitialRetryWait(500)
222
+ .withMaxRetryWait(30 * 1000)
223
+ .runInterruptible(new Retryable<Void>() {
224
+ @Override
225
+ public Void call() throws FTPIllegalReplyException, FTPException, FTPDataTransferException,
226
+ FTPAbortedException, IOException, RetryGiveupException
227
+ {
228
+ log.info("Upload start {} to {}", file.getAbsolutePath(), filePath);
229
+ client.upload(filePath, new BufferedInputStream(new FileInputStream(file)), 0L, 0L, new LoggingTransferListener(log, TRANSFER_NOTICE_BYTES));
230
+ log.info("Upload completed {} to {}", file.getAbsolutePath(), filePath);
231
+ if (!file.delete()) {
232
+ throw new ConfigException("Couldn't delete local file " + file.getAbsolutePath());
233
+ }
234
+ log.info("Delete local temporary file {}", file.getAbsolutePath());
235
+ return null;
236
+ }
237
+
238
+ @Override
239
+ public boolean isRetryableException(Exception exception)
240
+ {
241
+ return true;
242
+ }
243
+
244
+ @Override
245
+ public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait)
246
+ throws RetryGiveupException
247
+ {
248
+ if (exception instanceof FileNotFoundException || exception instanceof URISyntaxException || exception instanceof ConfigException) {
249
+ throw new RetryGiveupException(exception);
250
+ }
251
+ String message = String.format("FTP put request failed. Retrying %d/%d after %d seconds. Message: %s",
252
+ retryCount, retryLimit, retryWait / 1000, exception.getMessage());
253
+ if (retryCount % 3 == 0) {
254
+ log.warn(message, exception);
255
+ }
256
+ else {
257
+ log.warn(message);
258
+ }
259
+ }
260
+
261
+ @Override
262
+ public void onGiveup(Exception firstException, Exception lastException)
263
+ throws RetryGiveupException
264
+ {
265
+ }
266
+ });
267
+ }
268
+ catch (RetryGiveupException ex) {
269
+ throw Throwables.propagate(ex.getCause());
270
+ }
271
+ catch (InterruptedException ex) {
272
+ throw Throwables.propagate(ex);
273
+ }
274
+ }
275
+ return null;
276
+ }
277
+
278
+ @Override
279
+ public void close()
280
+ {
281
+ closeFile();
282
+ }
283
+
284
+ @Override
285
+ public void abort() {}
286
+
287
+ @Override
288
+ public TaskReport commit()
289
+ {
290
+ return Exec.newTaskReport();
291
+ }
292
+ }
293
+
294
+ private static FTPClient newFTPClient(Logger log, PluginTask task)
295
+ {
296
+ FTPClient client = new FTPClient();
297
+ try {
298
+ if (task.getSsl()) {
299
+ client.setSSLSocketFactory(SSLPlugins.newSSLSocketFactory(task.getSSLConfig(), task.getHost()));
300
+ client.setSecurity(FTPClient.SECURITY_FTPS);
301
+ if (!task.getPort().isPresent()) {
302
+ task.setPort(Optional.of(990));
303
+ }
304
+ }
305
+ else {
306
+ if (!task.getPort().isPresent()) {
307
+ task.setPort(Optional.of(21));
308
+ }
309
+ }
310
+
311
+ client.addCommunicationListener(new LoggingCommunicationListner(log));
312
+
313
+ // TODO configurable timeout parameters
314
+ client.setAutoNoopTimeout(3000);
315
+
316
+ FTPConnector con = client.getConnector();
317
+ con.setConnectionTimeout(30);
318
+ con.setReadTimeout(60);
319
+ con.setCloseTimeout(60);
320
+
321
+ // for commons-net client
322
+ //client.setControlKeepAliveTimeout
323
+ //client.setConnectTimeout
324
+ //client.setSoTimeout
325
+ //client.setDataTimeout
326
+ //client.setAutodetectUTF8
327
+
328
+ log.info("Connecting to {}", task.getHost());
329
+ if (task.getPort().isPresent()) {
330
+ client.connect(task.getHost(), task.getPort().get());
331
+ }
332
+
333
+ if (task.getUser().isPresent()) {
334
+ log.info("Logging in with user {}", task.getUser().get());
335
+ client.login(task.getUser().get(), task.getPassword().or(""));
336
+ }
337
+
338
+ log.info("Using passive mode");
339
+ client.setPassive(task.getPassiveMode());
340
+
341
+ if (task.getAsciiMode()) {
342
+ log.info("Using ASCII mode");
343
+ client.setType(FTPClient.TYPE_TEXTUAL);
344
+ }
345
+ else {
346
+ log.info("Using binary mode");
347
+ client.setType(FTPClient.TYPE_BINARY);
348
+ }
349
+
350
+ if (client.isCompressionSupported()) {
351
+ log.info("Using MODE Z compression");
352
+ client.setCompressionEnabled(true);
353
+ }
354
+
355
+ FTPClient connected = client;
356
+ client = null;
357
+ return connected;
358
+ }
359
+ catch (FTPException ex) {
360
+ log.info("FTP command failed: {}, {}", ex.getCode(), ex.getMessage());
361
+ throw Throwables.propagate(ex);
362
+ }
363
+ catch (FTPIllegalReplyException ex) {
364
+ log.info("FTP protocol error");
365
+ throw Throwables.propagate(ex);
366
+ }
367
+ catch (IOException ex) {
368
+ log.info("FTP network error: {}", ex);
369
+ throw Throwables.propagate(ex);
370
+ }
371
+ finally {
372
+ disconnectClient(client);
373
+ }
374
+ }
375
+
376
+ static void disconnectClient(FTPClient client)
377
+ {
378
+ if (client != null && client.isConnected()) {
379
+ try {
380
+ client.disconnect(false);
381
+ }
382
+ catch (FTPException | FTPIllegalReplyException | IOException ex) {
383
+ // do nothing
384
+ }
385
+ }
386
+ }
387
+
388
+ private static class LoggingCommunicationListner implements FTPCommunicationListener
389
+ {
390
+ private final Logger log;
391
+
392
+ public LoggingCommunicationListner(Logger log)
393
+ {
394
+ this.log = log;
395
+ }
396
+
397
+ public void received(String statement)
398
+ {
399
+ log.info("< " + statement);
400
+ }
401
+
402
+ public void sent(String statement)
403
+ {
404
+ if (statement.startsWith("PASS")) {
405
+ // don't show password
406
+ return;
407
+ }
408
+ log.info("> {}", statement);
409
+ }
410
+ }
411
+
412
+ private static class LoggingTransferListener implements FTPDataTransferListener
413
+ {
414
+ private final Logger log;
415
+ private final long transferNoticeBytes;
416
+
417
+ private long totalTransfer;
418
+ private long nextTransferNotice;
419
+
420
+ public LoggingTransferListener(Logger log, long transferNoticeBytes)
421
+ {
422
+ this.log = log;
423
+ this.transferNoticeBytes = transferNoticeBytes;
424
+ this.nextTransferNotice = transferNoticeBytes;
425
+ }
426
+
427
+ public void started()
428
+ {
429
+ log.info("Transfer started");
430
+ }
431
+
432
+ public void transferred(int length)
433
+ {
434
+ totalTransfer += length;
435
+ if (totalTransfer > nextTransferNotice) {
436
+ log.info("Transferred {} bytes", totalTransfer);
437
+ nextTransferNotice = ((totalTransfer / transferNoticeBytes) + 1) * transferNoticeBytes;
438
+ }
439
+ }
440
+
441
+ public void completed()
442
+ {
443
+ log.info("Transfer completed {} bytes", totalTransfer);
444
+ }
445
+
446
+ public void aborted()
447
+ {
448
+ log.info("Transfer aborted");
449
+ }
450
+
451
+ public void failed()
452
+ {
453
+ log.info("Transfer failed");
454
+ }
455
+ }
456
+ }
@@ -0,0 +1,260 @@
1
+ package org.embulk.output.ftp;
2
+
3
+ import com.fasterxml.jackson.annotation.JsonCreator;
4
+ import com.fasterxml.jackson.annotation.JsonIgnore;
5
+ import com.fasterxml.jackson.annotation.JsonProperty;
6
+ import com.google.common.base.Function;
7
+ import com.google.common.base.Optional;
8
+ import com.google.common.collect.ImmutableList;
9
+ import com.google.common.collect.Lists;
10
+ import org.embulk.config.Config;
11
+ import org.embulk.config.ConfigDefault;
12
+ import org.embulk.config.ConfigException;
13
+
14
+ import javax.net.ssl.SSLSocketFactory;
15
+ import javax.net.ssl.X509TrustManager;
16
+
17
+ import java.io.ByteArrayInputStream;
18
+ import java.io.FileReader;
19
+ import java.io.IOException;
20
+ import java.io.Reader;
21
+ import java.io.StringReader;
22
+ import java.security.GeneralSecurityException;
23
+ import java.security.KeyManagementException;
24
+ import java.security.cert.CertificateEncodingException;
25
+ import java.security.cert.CertificateException;
26
+ import java.security.cert.CertificateFactory;
27
+ import java.security.cert.X509Certificate;
28
+ import java.util.List;
29
+
30
+ public class SSLPlugins
31
+ {
32
+ // SSLPlugins is only for SSL clients. SSL server implementation is out ouf scope.
33
+
34
+ private SSLPlugins()
35
+ {
36
+ }
37
+
38
+ public interface SSLPluginTask
39
+ {
40
+ @Config("ssl_verify")
41
+ @ConfigDefault("null")
42
+ Optional<Boolean> getSslVerify();
43
+
44
+ @Config("ssl_verify_hostname")
45
+ @ConfigDefault("true")
46
+ boolean getSslVerifyHostname();
47
+
48
+ @Config("ssl_trusted_ca_cert_file")
49
+ @ConfigDefault("null")
50
+ Optional<String> getSslTrustedCaCertFile();
51
+
52
+ @Config("ssl_trusted_ca_cert_data")
53
+ @ConfigDefault("null")
54
+ Optional<String> getSslTrustedCaCertData();
55
+ }
56
+
57
+ private static enum VerifyMode
58
+ {
59
+ NO_VERIFY,
60
+ CERTIFICATES,
61
+ JVM_DEFAULT;
62
+ }
63
+
64
+ public static class SSLPluginConfig
65
+ {
66
+ static SSLPluginConfig noVerify = new SSLPluginConfig(VerifyMode.NO_VERIFY, false, ImmutableList.<byte[]>of());
67
+
68
+ private final VerifyMode verifyMode;
69
+ private final boolean verifyHostname;
70
+ private final List<X509Certificate> certificates;
71
+
72
+ @JsonCreator
73
+ private SSLPluginConfig(
74
+ @JsonProperty("verifyMode") VerifyMode verifyMode,
75
+ @JsonProperty("verifyHostname") boolean verifyHostname,
76
+ @JsonProperty("certificates") List<byte[]> certificates)
77
+ {
78
+ this.verifyMode = verifyMode;
79
+ this.verifyHostname = verifyHostname;
80
+ this.certificates = ImmutableList.copyOf(
81
+ Lists.transform(certificates, new Function<byte[], X509Certificate>() {
82
+ public X509Certificate apply(byte[] data)
83
+ {
84
+ try (ByteArrayInputStream in = new ByteArrayInputStream(data)) {
85
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
86
+ return (X509Certificate) cf.generateCertificate(in);
87
+ }
88
+ catch (IOException | CertificateException ex) {
89
+ throw new RuntimeException(ex);
90
+ }
91
+ }
92
+ })
93
+ );
94
+ }
95
+
96
+ SSLPluginConfig(List<X509Certificate> certificates, boolean verifyHostname)
97
+ {
98
+ this.verifyMode = VerifyMode.CERTIFICATES;
99
+ this.verifyHostname = verifyHostname;
100
+ this.certificates = certificates;
101
+ }
102
+
103
+ static SSLPluginConfig useJvmDefault(boolean verifyHostname)
104
+ {
105
+ return new SSLPluginConfig(VerifyMode.JVM_DEFAULT, verifyHostname, ImmutableList.<byte[]>of());
106
+ }
107
+
108
+ @JsonProperty("verifyMode")
109
+ private VerifyMode getVerifyMode()
110
+ {
111
+ return verifyMode;
112
+ }
113
+
114
+ @JsonProperty("verifyHostname")
115
+ private boolean getVerifyHostname()
116
+ {
117
+ return verifyHostname;
118
+ }
119
+
120
+ @JsonProperty("certificates")
121
+ private List<byte[]> getCertData()
122
+ {
123
+ return Lists.transform(certificates, new Function<X509Certificate, byte[]>() {
124
+ public byte[] apply(X509Certificate cert)
125
+ {
126
+ try {
127
+ return cert.getEncoded();
128
+ }
129
+ catch (CertificateEncodingException ex) {
130
+ throw new RuntimeException(ex);
131
+ }
132
+ }
133
+ });
134
+ }
135
+
136
+ @JsonIgnore
137
+ public X509TrustManager[] newTrustManager()
138
+ {
139
+ try {
140
+ switch (verifyMode) {
141
+ case NO_VERIFY:
142
+ return new X509TrustManager[] { getNoVerifyTrustManager() };
143
+ case CERTIFICATES:
144
+ return TrustManagers.newTrustManager(certificates);
145
+ default: // JVM_DEFAULT
146
+ return TrustManagers.newDefaultJavaTrustManager();
147
+ }
148
+ }
149
+ catch (IOException | GeneralSecurityException ex) {
150
+ throw new RuntimeException(ex);
151
+ }
152
+ }
153
+ }
154
+
155
+ public static enum DefaultVerifyMode
156
+ {
157
+ VERIFY_BY_JVM_TRUSTED_CA_CERTS,
158
+ NO_VERIFY;
159
+ }
160
+
161
+ public static SSLPluginConfig configure(SSLPluginTask task)
162
+ {
163
+ return configure(task, DefaultVerifyMode.VERIFY_BY_JVM_TRUSTED_CA_CERTS);
164
+ }
165
+
166
+ public static SSLPluginConfig configure(SSLPluginTask task, DefaultVerifyMode defaultVerifyMode)
167
+ {
168
+ boolean verify = task.getSslVerify().or(defaultVerifyMode != DefaultVerifyMode.NO_VERIFY);
169
+ if (verify) {
170
+ Optional<List<X509Certificate>> certs = readTrustedCertificates(task);
171
+ if (certs.isPresent()) {
172
+ return new SSLPluginConfig(certs.get(), task.getSslVerifyHostname());
173
+ }
174
+ else {
175
+ return SSLPluginConfig.useJvmDefault(task.getSslVerifyHostname());
176
+ }
177
+ }
178
+ else {
179
+ return SSLPluginConfig.noVerify;
180
+ }
181
+ }
182
+
183
+ private static Optional<List<X509Certificate>> readTrustedCertificates(SSLPluginTask task)
184
+ {
185
+ String optionName;
186
+ Reader reader;
187
+ if (task.getSslTrustedCaCertData().isPresent()) {
188
+ optionName = "ssl_trusted_ca_cert_data";
189
+ reader = new StringReader(task.getSslTrustedCaCertData().get());
190
+ }
191
+ else if (task.getSslTrustedCaCertFile().isPresent()) {
192
+ optionName = "ssl_trusted_ca_cert_file '" + task.getSslTrustedCaCertFile().get() + "'";
193
+ try {
194
+ reader = new FileReader(task.getSslTrustedCaCertFile().get());
195
+ }
196
+ catch (IOException ex) {
197
+ throw new ConfigException(String.format("Failed to open %s", optionName), ex);
198
+ }
199
+ }
200
+ else {
201
+ return Optional.absent();
202
+ }
203
+
204
+ List<X509Certificate> certs;
205
+ try (Reader r = reader) {
206
+ certs = TrustManagers.readPemEncodedX509Certificates(r);
207
+ if (certs.isEmpty()) {
208
+ throw new ConfigException(String.format("%s does not include valid X.509 PEM certificates", optionName));
209
+ }
210
+ }
211
+ catch (CertificateException | IOException ex) {
212
+ throw new ConfigException(String.format("Failed to read %s", optionName), ex);
213
+ }
214
+
215
+ return Optional.of(certs);
216
+ }
217
+
218
+ public static SSLSocketFactory newSSLSocketFactory(SSLPluginConfig config, String hostname)
219
+ {
220
+ try {
221
+ return TrustManagers.newSSLSocketFactory(
222
+ null, // TODO sending client certificate is not implemented yet
223
+ config.newTrustManager(),
224
+ config.getVerifyHostname() ? hostname : null);
225
+ }
226
+ catch (KeyManagementException ex) {
227
+ throw new RuntimeException(ex);
228
+ }
229
+ }
230
+
231
+ private static class NoVerifyTrustManager implements X509TrustManager
232
+ {
233
+ static final NoVerifyTrustManager INSTANCE = new NoVerifyTrustManager();
234
+
235
+ private NoVerifyTrustManager()
236
+ {
237
+ }
238
+
239
+ @Override
240
+ public X509Certificate[] getAcceptedIssuers()
241
+ {
242
+ return null;
243
+ }
244
+
245
+ @Override
246
+ public void checkClientTrusted(X509Certificate[] certs, String authType)
247
+ {
248
+ }
249
+
250
+ @Override
251
+ public void checkServerTrusted(X509Certificate[] certs, String authType)
252
+ {
253
+ }
254
+ }
255
+
256
+ private static X509TrustManager getNoVerifyTrustManager()
257
+ {
258
+ return NoVerifyTrustManager.INSTANCE;
259
+ }
260
+ }