View Javadoc

1   /**
2    * This file is part of Waarp Project.
3    * 
4    * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
5    * COPYRIGHT.txt in the distribution for a full listing of individual contributors.
6    * 
7    * All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
8    * the GNU General Public License as published by the Free Software Foundation, either version 3 of
9    * the License, or (at your option) any later version.
10   * 
11   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
12   * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13   * Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License along with Waarp . If not, see
16   * <http://www.gnu.org/licenses/>.
17   */
18  package org.waarp.ftp.core.data;
19  
20  import java.net.InetAddress;
21  import java.net.InetSocketAddress;
22  import java.util.List;
23  import java.util.concurrent.ExecutorService;
24  import java.util.concurrent.Executors;
25  import java.util.concurrent.TimeUnit;
26  
27  import io.netty.bootstrap.Bootstrap;
28  import io.netty.channel.Channel;
29  import io.netty.channel.ChannelFuture;
30  
31  import org.waarp.common.command.ReplyCode;
32  import org.waarp.common.command.exception.CommandAbstractException;
33  import org.waarp.common.command.exception.Reply425Exception;
34  import org.waarp.common.crypto.ssl.WaarpSslUtility;
35  import org.waarp.common.future.WaarpChannelFuture;
36  import org.waarp.common.future.WaarpFuture;
37  import org.waarp.common.logging.WaarpLogger;
38  import org.waarp.common.logging.WaarpLoggerFactory;
39  import org.waarp.ftp.core.command.FtpCommandCode;
40  import org.waarp.ftp.core.command.service.ABOR;
41  import org.waarp.ftp.core.config.FtpConfiguration;
42  import org.waarp.ftp.core.config.FtpInternalConfiguration;
43  import org.waarp.ftp.core.control.NetworkHandler;
44  import org.waarp.ftp.core.data.handler.DataNetworkHandler;
45  import org.waarp.ftp.core.exception.FtpNoConnectionException;
46  import org.waarp.ftp.core.exception.FtpNoFileException;
47  import org.waarp.ftp.core.exception.FtpNoTransferException;
48  import org.waarp.ftp.core.file.FtpFile;
49  import org.waarp.ftp.core.session.FtpSession;
50  
51  /**
52   * Main class that handles transfers and their execution
53   * 
54   * @author Frederic Bregier
55   * 
56   */
57  public class FtpTransferControl {
58      /**
59       * Internal Logger
60       */
61      private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(FtpTransferControl.class);
62  
63      /**
64       * SessionInterface
65       */
66      private final FtpSession session;
67  
68      /**
69       * Step in order to wait that the DataNetworkHandler is ready
70       */
71      private volatile boolean isDataNetworkHandlerReady = false;
72  
73      /**
74       * The associated DataChannel
75       */
76      private volatile Channel dataChannel = null;
77  
78      /**
79       * Waiter for the dataChannel to be opened
80       */
81      private volatile WaarpChannelFuture waitForOpenedDataChannel = new WaarpChannelFuture(true);
82  
83      /**
84       * Is the current Command Finished (or previously current command)
85       */
86      private volatile boolean isExecutingCommandFinished = true;
87      /**
88       * Waiter for the Command to be setup
89       */
90      private volatile WaarpFuture commandSetup = null;
91  
92      /**
93       * Waiter for the Command finishing
94       */
95      private volatile WaarpFuture commandFinishing = null;
96  
97      /**
98       * Current command executed
99       */
100     private volatile FtpTransfer executingCommand = null;
101 
102     /**
103      * Thread pool for execution of transfer command
104      */
105     private ExecutorService executorService = null;
106 
107     /**
108      * Blocking step for the Executor in order to wait for the end of the command (internal wait,
109      * not to be used outside).
110      */
111     private volatile WaarpFuture endOfCommand = null;
112 
113     /**
114      * A boolean to know if Check was called once
115      */
116     private volatile boolean isCheckAlreadyCalled = false;
117 
118     /**
119      * 
120      * @param session
121      */
122     public FtpTransferControl(FtpSession session) {
123         this.session = session;
124         endOfCommand = null;
125     }
126 
127     // XXX DataNetworkHandler functions
128     /**
129      * The DataNetworkHandler is ready (from setNewFtpExecuteTransfer)
130      * 
131      */
132     private void setDataNetworkHandlerReady() {
133         isCheckAlreadyCalled = false;
134         isDataNetworkHandlerReady = true;
135     }
136 
137     /**
138      * Wait for the DataNetworkHandler to be ready (from trueRetrieve of {@link FtpFile})
139      * 
140      * @throws InterruptedException
141      * 
142      */
143     public void waitForDataNetworkHandlerReady() throws InterruptedException {
144         if (!isDataNetworkHandlerReady) {
145             Thread.sleep(10);
146             if (!isDataNetworkHandlerReady) {
147                 // logger.debug("Wait for DataNetwork Ready over {}");
148                 throw new InterruptedException("Bad initialization");
149             }
150         }
151     }
152 
153     /**
154      * Set the new opened Channel (from channelConnected of {@link DataNetworkHandler})
155      * 
156      * @param channel
157      * @param dataNetworkHandler
158      */
159     public void setOpenedDataChannel(Channel channel,
160             DataNetworkHandler dataNetworkHandler) {
161         logger.debug("SetOpenedDataChannel: " + (channel != null ? channel.remoteAddress() : "no channel"));
162         if (channel != null) {
163             session.getDataConn().setDataNetworkHandler(dataNetworkHandler);
164             waitForOpenedDataChannel.setChannel(channel);
165             waitForOpenedDataChannel.setSuccess();
166         } else {
167             waitForOpenedDataChannel.cancel();
168         }
169     }
170 
171     /**
172      * Wait that the new opened connection is ready (same method in {@link FtpDataAsyncConn} from
173      * openConnection)
174      * 
175      * @return the new opened Channel
176      * @throws InterruptedException
177      */
178     public Channel waitForOpenedDataChannel() throws InterruptedException {
179         Channel channel = null;
180         if (waitForOpenedDataChannel.await(
181                 session.getConfiguration().getTIMEOUTCON() + 1000,
182                 TimeUnit.MILLISECONDS)) {
183             if (waitForOpenedDataChannel.isSuccess()) {
184                 channel = waitForOpenedDataChannel.channel();
185             } else {
186                 logger.warn("data connection is in error");
187             }
188         } else {
189             logger.warn("Timeout occurs during data connection");
190         }
191         return channel;
192     }
193 
194     /**
195      * Allow to reset the waitForOpenedDataChannel
196      */
197     public void resetWaitForOpenedDataChannel() {
198         if (waitForOpenedDataChannel != null) {
199             waitForOpenedDataChannel.cancel();
200         }
201         waitForOpenedDataChannel = new WaarpChannelFuture(true);
202     }
203 
204     /**
205      * Wait for the client to be connected (Passive) or Wait for the server to be connected to the
206      * client (Active)
207      * 
208      * @return True if the connection is OK
209      * @throws Reply425Exception
210      */
211     public synchronized boolean openDataConnection() throws Reply425Exception {
212         // Prepare this Data channel to be closed ;-)
213         // In fact, prepare the future close op which should occur since it is
214         // now opened
215         if (commandSetup != null) {
216             commandSetup.cancel();
217         }
218         commandSetup = new WaarpFuture(true);
219         FtpDataAsyncConn dataAsyncConn = session.getDataConn();
220         if (!dataAsyncConn.isStreamFile()) {
221             // FIXME isActive or isDNHReady ?
222             if (dataAsyncConn.isActive()) {
223                 // Already connected
224                 logger.debug("Connection already open");
225                 session.setReplyCode(
226                         ReplyCode.REPLY_125_DATA_CONNECTION_ALREADY_OPEN,
227                         dataAsyncConn.getType().name() +
228                                 " mode data connection already open");
229                 return true;
230             }
231         } else {
232             // Stream, Data Connection should not be opened
233             if (dataAsyncConn.isActive()) {
234                 logger
235                         .error("Connection already open but should not since in Stream mode");
236                 setTransferAbortedFromInternal(false);
237                 throw new Reply425Exception(
238                         "Connection already open but should not since in Stream mode");
239             }
240         }
241         // Need to open connection
242         session.setReplyCode(ReplyCode.REPLY_150_FILE_STATUS_OKAY, "Opening " +
243                 dataAsyncConn.getType().name() + " mode data connection");
244         if (dataAsyncConn.isPassiveMode()) {
245             if (!dataAsyncConn.isBind()) {
246                 // No passive connection prepared
247                 throw new Reply425Exception(
248                         "No passive data connection prepared");
249             }
250             // Wait for the connection to be done by the client
251             logger.debug("Passive mode standby");
252             try {
253                 dataChannel = waitForOpenedDataChannel();
254                 dataAsyncConn.setNewOpenedDataChannel(dataChannel);
255             } catch (InterruptedException e) {
256                 logger.warn("Connection abort in passive mode", e);
257                 // Cannot open connection
258                 throw new Reply425Exception(
259                         "Cannot open passive data connection");
260             }
261             logger.debug("Passive mode connected");
262         } else {
263             // Wait for the server to be connected to the client
264             InetAddress inetAddress = dataAsyncConn.getLocalAddress().getAddress();
265             InetSocketAddress inetSocketAddress = dataAsyncConn.getRemoteAddress();
266             if (session.getConfiguration().getFtpInternalConfiguration().hasFtpSession(inetAddress, inetSocketAddress)) {
267                 throw new Reply425Exception(
268                         "Cannot open active data connection since remote address is already in use: "
269                                 +
270                                 inetSocketAddress);
271             }
272             logger.debug("Active mode standby");
273             Bootstrap bootstrap = session.getConfiguration().getFtpInternalConfiguration()
274                     .getActiveBootstrap(session.isDataSsl());
275             session.getConfiguration().setNewFtpSession(inetAddress, inetSocketAddress, session);
276             // Set the session for the future dataChannel
277             String mylog = session.toString();
278             logger.debug("DataConn for: " + session.getCurrentCommand().getCommand() + " to "
279                     + inetSocketAddress.toString());
280             ChannelFuture future = bootstrap.connect(inetSocketAddress, dataAsyncConn.getLocalAddress());
281             try {
282                 future.await();
283             } catch (InterruptedException e1) {
284             }
285             if (!future.isSuccess()) {
286                 logger.warn("Connection abort in active mode from future while session: " +
287                         session.toString() +
288                         "\nTrying connect to: " + inetSocketAddress.toString() +
289                         " From: " + dataAsyncConn.getLocalAddress() +
290                         "\nWas: " + mylog,
291                         future.cause());
292                 // Cannot open connection
293                 session.getConfiguration().delFtpSession(inetAddress,
294                         inetSocketAddress);
295                 throw new Reply425Exception(
296                         "Cannot open active data connection");
297             }
298             try {
299                 dataChannel = waitForOpenedDataChannel();
300                 dataAsyncConn.setNewOpenedDataChannel(dataChannel);
301             } catch (InterruptedException e) {
302                 logger.warn("Connection abort in active mode", e);
303                 // Cannot open connection
304                 session.getConfiguration().delFtpSession(inetAddress,
305                         inetSocketAddress);
306                 throw new Reply425Exception(
307                         "Cannot open active data connection");
308             }
309             logger.debug("Active mode connected");
310         }
311         if (dataChannel == null) {
312             // Cannot have a new Data connection since shutdown
313             if (!dataAsyncConn.isPassiveMode()) {
314                 session.getConfiguration().getFtpInternalConfiguration()
315                         .delFtpSession(
316                                 dataAsyncConn.getLocalAddress().getAddress(),
317                                 dataAsyncConn.getRemoteAddress());
318             }
319             throw new Reply425Exception(
320                     "Cannot open data connection, shuting down");
321         }
322         return true;
323     }
324 
325     // XXX FtpTransfer functions
326     /**
327      * Run the command from an executor
328      */
329     private void runExecutor() {
330         endOfCommand = new WaarpFuture(true);
331         try {
332             session.getDataConn().getDataNetworkHandler().setFtpTransfer(executingCommand);
333         } catch (FtpNoConnectionException e1) {
334         }
335         waitForOpenedDataChannel.channel().config().setAutoRead(true);
336         /*
337         final WaarpFuture toFinish = commandFinishing;
338         final WaarpFuture toCommand = endOfCommand;
339          try {
340             session.getDataConn().getCurrentDataChannel().closeFuture()
341                     .addListener(new GenericFutureListener<Future<? super Void>>() {
342                         public void operationComplete(Future<? super Void> future) throws Exception {
343                             if (!toFinish.isDone() || !toCommand.isDone()) {
344                                 logger.debug("Schedule to finish command: " + session + ":" + toFinish.isDone() + ":"
345                                         + toCommand.isDone());
346                                 scheduleService.schedule(new Runnable() {
347                                     public void run() {
348                                         if (!toFinish.isDone() || !toCommand.isDone()) {
349                                             logger.warn("Will try to finish command: " + session + " CommandFinishing:"
350                                                     + toFinish.isDone() + " EndOfCommand:" + toCommand.isDone());
351                                             setTransferAbortedFromInternal(true);
352                                             //toFinish.cancel();
353                                         }
354                                     }
355                                 }, FtpConfiguration.DATATIMEOUTCON * 2, TimeUnit.MILLISECONDS);
356                             }
357                         }
358                     });
359         } catch (FtpNoConnectionException e1) {
360             //e1.printStackTrace();
361         }*/
362         // Run the command
363         if (executorService == null) {
364             executorService = Executors.newSingleThreadExecutor();
365         }
366         executorService.execute(new FtpTransferExecutor(session,
367                 executingCommand));
368         try {
369             commandFinishing.await();
370             if (commandFinishing.isFailed()) {
371                 endOfCommand.cancel();
372             }
373         } catch (InterruptedException e) {
374         }
375     }
376 
377     /**
378      * Add a new transfer to be executed. This is to be called from Command after connection is
379      * opened and before answering to the client that command is ready to be executed (for Store or
380      * Retrieve like operations).
381      * 
382      * @param command
383      * @param file
384      */
385     public void setNewFtpTransfer(FtpCommandCode command, FtpFile file) {
386         isExecutingCommandFinished = false;
387         commandFinishing = new WaarpFuture(true);
388         logger.debug("setNewCommand: {}", command);
389         setDataNetworkHandlerReady();
390         executingCommand = new FtpTransfer(command, file);
391         runExecutor();
392         commandFinishing = null;
393         commandSetup.setSuccess();
394         if (!session.getDataConn().isStreamFile()) {
395             waitForOpenedDataChannel.channel().config().setAutoRead(false);
396         }
397     }
398 
399     /**
400      * Add a new transfer to be executed. This is to be called from Command after connection is
401      * opened and before answering to the client that command is ready to be executed (for List like
402      * operations).
403      * 
404      * @param command
405      * @param list
406      * @param path
407      *            as Original Path
408      */
409     public void setNewFtpTransfer(FtpCommandCode command, List<String> list,
410             String path) {
411         isExecutingCommandFinished = false;
412         commandFinishing = new WaarpFuture(true);
413         logger.debug("setNewCommand: {}", command);
414         setDataNetworkHandlerReady();
415         executingCommand = new FtpTransfer(command, list, path);
416         runExecutor();
417         commandFinishing = null;
418         commandSetup.setSuccess();
419         if (!session.getDataConn().isStreamFile()) {
420             waitForOpenedDataChannel.channel().config().setAutoRead(false);
421         }
422         try {
423             session.getDataConn().getDataNetworkHandler().setFtpTransfer(null);
424         } catch (FtpNoConnectionException e1) {
425         }
426     }
427 
428     public boolean waitFtpTransferExecuting() {
429         boolean notFinished = true;
430         for (int i = 0; i < FtpInternalConfiguration.RETRYNB * 100; i++) {
431             if (isExecutingCommandFinished
432                     || commandFinishing == null
433                     || session.isCurrentCommandFinished()
434                     ||
435                     (commandFinishing != null && commandFinishing
436                             .awaitUninterruptibly(FtpInternalConfiguration.RETRYINMS))) {
437                 notFinished = false;
438                 break;
439             }
440         }
441         return notFinished;
442     }
443 
444     /**
445      * Is a command currently executing (called from {@link NetworkHandler} when a message is
446      * received to see if another transfer command is already in execution, which is not allowed)
447      * 
448      * @return True if a command is currently executing
449      */
450     public boolean isFtpTransferExecuting() {
451         return !isExecutingCommandFinished;
452     }
453 
454     /**
455      * 
456      * @return the current executing FtpTransfer
457      * @throws FtpNoTransferException
458      */
459     public FtpTransfer getExecutingFtpTransfer() throws FtpNoTransferException {
460         if (executingCommand != null) {
461             return executingCommand;
462         }
463         throw new FtpNoTransferException("No Command currently running");
464     }
465 
466     /**
467      * 
468      * @return True if the current FtpTransfer is a Retrieve like transfer
469      * @throws FtpNoTransferException
470      * @throws CommandAbstractException
471      * @throws FtpNoFileException
472      */
473     boolean isExecutingRetrLikeTransfer()
474             throws FtpNoTransferException, CommandAbstractException,
475             FtpNoFileException {
476         return !session.isCurrentCommandFinished() &&
477                 FtpCommandCode.isRetrLikeCommand(getExecutingFtpTransfer()
478                         .getCommand()) &&
479                 getExecutingFtpTransfer().getFtpFile().isInReading();
480     }
481 
482     /**
483      * Called when a transfer is finished from setEndOfTransfer
484      * 
485      * @return True if it was already called before
486      * @throws FtpNoTransferException
487      */
488     private boolean checkFtpTransferStatus() throws FtpNoTransferException {
489         if (isCheckAlreadyCalled) {
490             logger.warn("Check: ALREADY CALLED");
491             return true;
492         }
493         if (isExecutingCommandFinished) {
494             // already done
495             logger.warn("Check: already Finished");
496             if (commandFinishing != null) {
497                 commandFinishing.cancel();
498             }
499             throw new FtpNoTransferException("No transfer running");
500         }
501         if (!isDataNetworkHandlerReady) {
502             // already done
503             logger.warn("Check: already DNH not ready");
504             throw new FtpNoTransferException("No connection");
505         }
506         isCheckAlreadyCalled = true;
507         FtpTransfer executedTransfer = getExecutingFtpTransfer();
508         logger.debug("Check: command {}", executedTransfer.getCommand());
509         // DNH is ready and Transfer is running
510         if (FtpCommandCode.isListLikeCommand(executedTransfer.getCommand())) {
511             if (executedTransfer.getStatus()) {
512                 // Special status for List Like command
513                 logger.debug("Check: List OK");
514                 closeTransfer();
515                 return false;
516             }
517             logger.debug("Check: List Ko");
518             abortTransfer();
519             return false;
520         } else if (FtpCommandCode.isRetrLikeCommand(executedTransfer
521                 .getCommand())) {
522             FtpFile file = null;
523             try {
524                 file = executedTransfer.getFtpFile();
525             } catch (FtpNoFileException e) {
526                 logger.debug("Check: Retr no FtpFile for Retr");
527                 abortTransfer();
528                 return false;
529             }
530             try {
531                 if (file.isInReading()) {
532                     logger
533                             .debug("Check: Retr FtpFile still in reading KO");
534                     abortTransfer();
535                 } else {
536                     logger
537                             .debug("Check: Retr FtpFile no more in reading OK");
538                     closeTransfer();
539                 }
540             } catch (CommandAbstractException e) {
541                 logger.warn("Retr Test is in Reading problem", e);
542                 closeTransfer();
543             }
544             return false;
545         } else if (FtpCommandCode.isStoreLikeCommand(executedTransfer
546                 .getCommand())) {
547             // logger.debug("Check: Store OK");
548             closeTransfer();
549             return false;
550         } else {
551             logger.warn("Check: Unknown command");
552             abortTransfer();
553         }
554         return false;
555     }
556 
557     /**
558      * Abort the current transfer
559      */
560     private void abortTransfer() {
561         logger.debug("Will abort transfer and write: ", new Exception("trace only"));
562         FtpFile file = null;
563         FtpTransfer current = null;
564         try {
565             current = getExecutingFtpTransfer();
566             file = current.getFtpFile();
567             file.abortFile();
568         } catch (FtpNoTransferException e) {
569             logger.warn("Abort problem", e);
570         } catch (FtpNoFileException e) {
571         } catch (CommandAbstractException e) {
572             logger.warn("Abort problem", e);
573         }
574         if (current != null) {
575             current.setStatus(false);
576         }
577         endDataConnection();
578         session.setReplyCode(
579                 ReplyCode.REPLY_426_CONNECTION_CLOSED_TRANSFER_ABORTED,
580                 "Transfer aborted for " +
581                         (current == null ? "Unknown command" : current
582                                 .toString()));
583         if (current != null) {
584             if (!FtpCommandCode.isListLikeCommand(current.getCommand())) {
585                 try {
586                     session.getBusinessHandler().afterTransferDoneBeforeAnswer(current);
587                 } catch (CommandAbstractException e) {
588                     session.setReplyCode(e);
589                 }
590             }
591         }
592         finalizeExecution();
593     }
594 
595     /**
596      * Finish correctly a transfer
597      * 
598      */
599     private void closeTransfer() {
600         logger.debug("Will close transfer");
601         FtpFile file = null;
602         FtpTransfer current = null;
603         try {
604             current = getExecutingFtpTransfer();
605             file = current.getFtpFile();
606             file.closeFile();
607         } catch (FtpNoTransferException e) {
608             logger.warn("Close problem", e);
609         } catch (FtpNoFileException e) {
610         } catch (CommandAbstractException e) {
611             logger.warn("Close problem", e);
612         }
613         if (current != null) {
614             current.setStatus(true);
615         }
616         if (session.getDataConn().isStreamFile()) {
617             endDataConnection();
618         }
619         session.setReplyCode(ReplyCode.REPLY_226_CLOSING_DATA_CONNECTION,
620                 "Transfer complete for " +
621                         (current == null ? "Unknown command" : current
622                                 .toString()));
623         if (current != null) {
624             if (!FtpCommandCode.isListLikeCommand(current.getCommand())) {
625                 try {
626                     session.getBusinessHandler().afterTransferDoneBeforeAnswer(current);
627                 } catch (CommandAbstractException e) {
628                     session.setReplyCode(e);
629                 }
630             } else {
631                 // Special wait to prevent fast LIST following by STOR or RETR command
632                 try {
633                     Thread.sleep(FtpInternalConfiguration.RETRYINMS);
634                 } catch (InterruptedException e) {
635                 }
636             }
637         }
638         finalizeExecution();
639     }
640 
641     /**
642      * Set the current transfer as finished. Called from {@link FtpTransferExecutor} when a transfer
643      * is over.
644      * 
645      */
646     public void setEndOfTransfer() {
647         try {
648             checkFtpTransferStatus();
649         } catch (FtpNoTransferException e) {
650             return;
651         }
652     }
653 
654     /**
655      * To enable abort from internal error
656      * 
657      * @param write
658      *            True means the message is write back to the control command, false it is only
659      *            prepared
660      */
661     public void setTransferAbortedFromInternal(boolean write) {
662         logger.debug("Set transfer aborted internal {}", write);
663         abortTransfer();
664         if (write) {
665             session.getNetworkHandler().writeIntermediateAnswer();
666         }
667         if (endOfCommand != null) {
668             endOfCommand.cancel();
669         }
670     }
671 
672     /**
673      * Called by channelClosed (from {@link DataNetworkHandler} ) or trueRetrieve
674      * (from {@link FtpFile}) when the transfer is over
675      */
676     public void setPreEndOfTransfer() {
677         if (endOfCommand != null) {
678             endOfCommand.setSuccess();
679             logger.debug("Transfer completed");
680         }
681     }
682 
683     /**
684      * Wait for the current transfer to finish, called from {@link FtpTransferExecutor}
685      * 
686      * @throws InterruptedException
687      */
688     public void waitForEndOfTransfer() throws InterruptedException {
689         if (endOfCommand != null) {
690             endOfCommand.await();
691             if (endOfCommand.isFailed()) {
692                 throw new InterruptedException("Transfer aborted");
693             }
694         }
695         // logger.debug("waitEndOfCommand over");
696     }
697 
698     // XXX ExecutorHandler functions
699     /**
700      * Finalize execution
701      * 
702      */
703     private void finalizeExecution() {
704         // logger.debug("Finalize execution");
705         if (commandFinishing != null) {
706             commandFinishing.setSuccess();
707         }
708         isExecutingCommandFinished = true;
709         executingCommand = null;
710         resetWaitForOpenedDataChannel();
711     }
712 
713     // XXX Finalize of Transfer
714     /**
715      * End the data connection if any
716      */
717     private synchronized void endDataConnection() {
718         logger.debug("End Data connection");
719         if (isDataNetworkHandlerReady && dataChannel != null) {
720             try {
721                 WaarpSslUtility.closingSslChannel(dataChannel).await(FtpConfiguration.getDATATIMEOUTCON(),
722                         TimeUnit.MILLISECONDS);
723             } catch (InterruptedException e) {
724             }
725             isDataNetworkHandlerReady = false;
726             // logger.debug("waitForClosedDataChannel over");
727             dataChannel = null;
728         }
729     }
730 
731     /**
732      * Clear the FtpTransferControl (called when the data connection must be over like from clear of {@link FtpDataAsyncConn},
733      * abort from {@link ABOR} or ending control connection from {@link NetworkHandler}.
734      * 
735      */
736     public void clear() {
737         // logger.debug("Clear Ftp Transfer Control");
738         endDataConnection();
739         finalizeExecution();
740         if (endOfCommand != null) {
741             endOfCommand.cancel();
742         }
743         if (waitForOpenedDataChannel != null) {
744             waitForOpenedDataChannel.cancel();
745         }
746         if (commandSetup != null) {
747             commandSetup.cancel();
748         }
749         if (executorService != null) {
750             executorService.shutdownNow();
751             executorService = null;
752         }
753     }
754 }