Coverage Report - org.mule.transport.ftp.FtpConnector
 
Classes in this File Line Coverage Branch Coverage Complexity
FtpConnector
0%
0/188
0%
0/82
0
FtpConnector$1
0%
0/8
0%
0/2
0
 
 1  
 /*
 2  
  * $Id: FtpConnector.java 19640 2010-09-13 22:00:05Z tcarlson $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 9  
  */
 10  
 
 11  
 package org.mule.transport.ftp;
 12  
 
 13  
 import org.mule.api.MuleContext;
 14  
 import org.mule.api.MuleEvent;
 15  
 import org.mule.api.MuleException;
 16  
 import org.mule.api.MuleMessage;
 17  
 import org.mule.api.MuleRuntimeException;
 18  
 import org.mule.api.config.ThreadingProfile;
 19  
 import org.mule.api.construct.FlowConstruct;
 20  
 import org.mule.api.endpoint.EndpointURI;
 21  
 import org.mule.api.endpoint.ImmutableEndpoint;
 22  
 import org.mule.api.endpoint.InboundEndpoint;
 23  
 import org.mule.api.endpoint.OutboundEndpoint;
 24  
 import org.mule.api.lifecycle.InitialisationException;
 25  
 import org.mule.api.transport.ConnectorException;
 26  
 import org.mule.api.transport.DispatchException;
 27  
 import org.mule.api.transport.MessageReceiver;
 28  
 import org.mule.config.i18n.CoreMessages;
 29  
 import org.mule.config.i18n.MessageFactory;
 30  
 import org.mule.model.streaming.CallbackOutputStream;
 31  
 import org.mule.transport.AbstractConnector;
 32  
 import org.mule.transport.ConnectException;
 33  
 import org.mule.transport.file.ExpressionFilenameParser;
 34  
 import org.mule.transport.file.FilenameParser;
 35  
 import org.mule.util.ClassUtils;
 36  
 
 37  
 import java.io.IOException;
 38  
 import java.io.OutputStream;
 39  
 import java.text.MessageFormat;
 40  
 import java.util.ArrayList;
 41  
 import java.util.HashMap;
 42  
 import java.util.List;
 43  
 import java.util.Map;
 44  
 
 45  
 import org.apache.commons.net.ftp.FTPClient;
 46  
 import org.apache.commons.net.ftp.FTPFile;
 47  
 import org.apache.commons.pool.ObjectPool;
 48  
 import org.apache.commons.pool.impl.GenericObjectPool;
 49  
 
 50  
 public class FtpConnector extends AbstractConnector
 51  
 {
 52  
 
 53  
     public static final String FTP = "ftp";
 54  
 
 55  
     // endpoint properties
 56  
     public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency"; // inbound only
 57  
     public static final int DEFAULT_POLLING_FREQUENCY = 1000;
 58  
     public static final String PROPERTY_OUTPUT_PATTERN = "outputPattern"; // outbound only
 59  
     public static final String PROPERTY_PASSIVE_MODE = "passive";
 60  
     public static final String PROPERTY_BINARY_TRANSFER = "binary";
 61  
 
 62  
     // message properties
 63  
     public static final String PROPERTY_FILENAME = "filename";
 64  
 
 65  
 
 66  
     /**
 67  
      *  TODO it makes sense to have a type-safe adapter for FTP specifically, but without
 68  
      *  Java 5's covariant return types the benefits are diminished. Keeping it simple for now.
 69  
      */
 70  
     public static final String DEFAULT_FTP_CONNECTION_FACTORY_CLASS = "org.mule.transport.ftp.FtpConnectionFactory";
 71  
 
 72  
     /**
 73  
      * Time in milliseconds to poll. On each poll the poll() method is called
 74  
      */
 75  
     private long pollingFrequency;
 76  
 
 77  
     private String outputPattern;
 78  
 
 79  0
     private FilenameParser filenameParser = new ExpressionFilenameParser();
 80  
 
 81  0
     private boolean passive = true;
 82  
 
 83  0
     private boolean binary = true;
 84  
 
 85  
     /** Streaming is off by default until MULE-3192 gets fixed */
 86  0
     private boolean streaming = false;
 87  
     
 88  
     private Map<String, ObjectPool> pools;
 89  
 
 90  0
     private String connectionFactoryClass = DEFAULT_FTP_CONNECTION_FACTORY_CLASS;
 91  
 
 92  
     public FtpConnector(MuleContext context)
 93  
     {
 94  0
         super(context);
 95  0
     }
 96  
     
 97  
     public String getProtocol()
 98  
     {
 99  0
         return FTP;
 100  
     }
 101  
 
 102  
     @Override
 103  
     public MessageReceiver createReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint) throws Exception
 104  
     {
 105  0
         List args = getReceiverArguments(endpoint.getProperties());
 106  0
         return serviceDescriptor.createMessageReceiver(this, flowConstruct, endpoint, args.toArray());
 107  
     }
 108  
 
 109  
     protected List getReceiverArguments(Map endpointProperties)
 110  
     {
 111  0
         List args = new ArrayList();
 112  
         
 113  0
         long polling = getPollingFrequency();
 114  0
         if (endpointProperties != null)
 115  
         {
 116  
             // Override properties on the endpoint for the specific endpoint
 117  0
             String tempPolling = (String) endpointProperties.get(PROPERTY_POLLING_FREQUENCY);
 118  0
             if (tempPolling != null)
 119  
             {
 120  0
                 polling = Long.parseLong(tempPolling);
 121  
             }
 122  
         }
 123  0
         if (polling <= 0)
 124  
         {
 125  0
             polling = DEFAULT_POLLING_FREQUENCY;
 126  
         }
 127  0
         logger.debug("set polling frequency to " + polling);
 128  0
         args.add(polling);
 129  
         
 130  0
         return args;
 131  
     }
 132  
     
 133  
     /**
 134  
      * @return Returns the pollingFrequency.
 135  
      */
 136  
     public long getPollingFrequency()
 137  
     {
 138  0
         return pollingFrequency;
 139  
     }
 140  
 
 141  
     /**
 142  
      * @param pollingFrequency The pollingFrequency to set.
 143  
      */
 144  
     public void setPollingFrequency(long pollingFrequency)
 145  
     {
 146  0
         this.pollingFrequency = pollingFrequency;
 147  0
     }
 148  
 
 149  
     /**
 150  
      * Getter for property 'connectionFactoryClass'.
 151  
      *
 152  
      * @return Value for property 'connectionFactoryClass'.
 153  
      */
 154  
     public String getConnectionFactoryClass()
 155  
     {
 156  0
         return connectionFactoryClass;
 157  
     }
 158  
 
 159  
     /**
 160  
      * Setter for property 'connectionFactoryClass'. Should be an instance of
 161  
      * {@link FtpConnectionFactory}.
 162  
      *
 163  
      * @param connectionFactoryClass Value to set for property 'connectionFactoryClass'.
 164  
      */
 165  
     public void setConnectionFactoryClass(final String connectionFactoryClass)
 166  
     {
 167  0
         this.connectionFactoryClass = connectionFactoryClass;
 168  0
     }
 169  
 
 170  
     public FTPClient getFtp(EndpointURI uri) throws Exception
 171  
     {
 172  0
         if (logger.isDebugEnabled())
 173  
         {
 174  0
             logger.debug(">>> retrieving client for " + uri);
 175  
         }
 176  0
         return (FTPClient) getFtpPool(uri).borrowObject();
 177  
     }
 178  
 
 179  
     public void releaseFtp(EndpointURI uri, FTPClient client) throws Exception
 180  
     {
 181  0
         if (logger.isDebugEnabled())
 182  
         {
 183  0
             logger.debug("<<< releasing client for " + uri);
 184  
         }
 185  0
         if (dispatcherFactory.isCreateDispatcherPerRequest())
 186  
         {
 187  0
             destroyFtp(uri, client);
 188  
         }
 189  
         else
 190  
         {
 191  0
             getFtpPool(uri).returnObject(client);
 192  
         }
 193  0
     }
 194  
 
 195  
     public void destroyFtp(EndpointURI uri, FTPClient client) throws Exception
 196  
     {
 197  0
         if (logger.isDebugEnabled())
 198  
         {
 199  0
             logger.debug("<<< destroying client for " + uri);
 200  
         }
 201  
         try
 202  
         {
 203  0
             getFtpPool(uri).invalidateObject(client);
 204  
         }
 205  0
         catch (Exception e)
 206  
         {
 207  
             // no way to test if pool is closed except try to access it
 208  0
             logger.debug(e.getMessage());
 209  0
         }
 210  0
     }
 211  
 
 212  
     protected synchronized ObjectPool getFtpPool(EndpointURI uri)
 213  
     {
 214  0
         if (logger.isDebugEnabled())
 215  
         {
 216  0
             logger.debug("=== get pool for " + uri);
 217  
         }
 218  0
         String key = uri.getUser() + ":" + uri.getPassword() + "@" + uri.getHost() + ":" + uri.getPort();
 219  0
         ObjectPool pool = pools.get(key);
 220  0
         if (pool == null)
 221  
         {
 222  
             try
 223  
             {
 224  0
                 FtpConnectionFactory connectionFactory =
 225  
                         (FtpConnectionFactory) ClassUtils.instanciateClass(getConnectionFactoryClass(),
 226  
                                                                             new Object[] {uri}, getClass());
 227  0
                 GenericObjectPool genericPool = createPool(connectionFactory);
 228  0
                 pools.put(key, genericPool);
 229  0
                 pool = genericPool;
 230  
             }
 231  0
             catch (Exception ex)
 232  
             {
 233  0
                 throw new MuleRuntimeException(
 234  
                         MessageFactory.createStaticMessage("Hmm, couldn't instanciate FTP connection factory."), ex);
 235  0
             }
 236  
         }
 237  0
         return pool;
 238  
     }
 239  
 
 240  
     protected GenericObjectPool createPool(FtpConnectionFactory connectionFactory)
 241  
     {
 242  0
         GenericObjectPool genericPool = new GenericObjectPool(connectionFactory);
 243  0
         byte poolExhaustedAction = ThreadingProfile.DEFAULT_POOL_EXHAUST_ACTION;
 244  
         
 245  0
         ThreadingProfile receiverThreadingProfile = this.getReceiverThreadingProfile();
 246  0
         if (receiverThreadingProfile != null) 
 247  
         {
 248  0
             int threadingProfilePoolExhaustedAction = receiverThreadingProfile.getPoolExhaustedAction();
 249  0
             if (threadingProfilePoolExhaustedAction == ThreadingProfile.WHEN_EXHAUSTED_WAIT) 
 250  
             {
 251  0
                 poolExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
 252  
             } 
 253  0
             else if (threadingProfilePoolExhaustedAction == ThreadingProfile.WHEN_EXHAUSTED_ABORT) 
 254  
             {
 255  0
                 poolExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
 256  
             } 
 257  0
             else if (threadingProfilePoolExhaustedAction == ThreadingProfile.WHEN_EXHAUSTED_RUN) 
 258  
             {
 259  0
                 poolExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
 260  
             }
 261  
         }
 262  
         
 263  0
         genericPool.setWhenExhaustedAction(poolExhaustedAction);
 264  0
         genericPool.setTestOnBorrow(isValidateConnections());
 265  0
         return genericPool;
 266  
     }
 267  
 
 268  
     @Override
 269  
     protected void doInitialise() throws InitialisationException
 270  
     {
 271  0
         if (filenameParser != null)
 272  
         {
 273  0
             filenameParser.setMuleContext(muleContext);
 274  
         }
 275  
 
 276  
         try
 277  
         {
 278  0
             Class objectFactoryClass = ClassUtils.loadClass(this.connectionFactoryClass, getClass());
 279  0
             if (!FtpConnectionFactory.class.isAssignableFrom(objectFactoryClass))
 280  
             {
 281  0
                 throw new InitialisationException(MessageFactory.createStaticMessage(
 282  
                         "FTP connectionFactoryClass is not an instance of org.mule.transport.ftp.FtpConnectionFactory"),
 283  
                         this);
 284  
             }
 285  
         }
 286  0
         catch (ClassNotFoundException e)
 287  
         {
 288  0
             throw new InitialisationException(e, this);
 289  0
         }
 290  
 
 291  0
         pools = new HashMap<String, ObjectPool>();
 292  0
     }
 293  
 
 294  
     @Override
 295  
     protected void doDispose()
 296  
     {
 297  
         // template method
 298  0
     }
 299  
 
 300  
     @Override
 301  
     protected void doConnect() throws Exception
 302  
     {
 303  
         // template method
 304  0
     }
 305  
 
 306  
     @Override
 307  
     protected void doDisconnect() throws Exception
 308  
     {
 309  
         // template method
 310  0
     }
 311  
 
 312  
     @Override
 313  
     protected void doStart() throws MuleException
 314  
     {
 315  
         // template method
 316  0
     }
 317  
 
 318  
     @Override
 319  
     protected void doStop() throws MuleException
 320  
     {
 321  0
         if (logger.isDebugEnabled())
 322  
         {
 323  0
             logger.debug("Stopping all pools");
 324  
         }
 325  
         try
 326  
         {
 327  0
             for (ObjectPool pool : pools.values())
 328  
             {
 329  0
                 pool.close();
 330  
             }
 331  
         }
 332  0
         catch (Exception e)
 333  
         {
 334  0
             throw new ConnectorException(CoreMessages.failedToStop("FTP Connector"), this, e);
 335  
         }
 336  
         finally
 337  
         {
 338  0
             pools.clear();
 339  0
         }
 340  0
     }
 341  
 
 342  
     /**
 343  
      * @return Returns the outputPattern.
 344  
      */
 345  
     public String getOutputPattern()
 346  
     {
 347  0
         return outputPattern;
 348  
     }
 349  
 
 350  
     /**
 351  
      * @param outputPattern The outputPattern to set.
 352  
      */
 353  
     public void setOutputPattern(String outputPattern)
 354  
     {
 355  0
         this.outputPattern = outputPattern;
 356  0
     }
 357  
 
 358  
     /**
 359  
      * @return Returns the filenameParser.
 360  
      */
 361  
     public FilenameParser getFilenameParser()
 362  
     {
 363  0
         return filenameParser;
 364  
     }
 365  
 
 366  
     /**
 367  
      * @param filenameParser The filenameParser to set.
 368  
      */
 369  
     public void setFilenameParser(FilenameParser filenameParser)
 370  
     {
 371  0
         this.filenameParser = filenameParser;
 372  0
         if (filenameParser != null)
 373  
         {
 374  0
             filenameParser.setMuleContext(muleContext);
 375  
         }
 376  0
     }
 377  
 
 378  
     /**
 379  
      * Getter for FTP passive mode.
 380  
      * 
 381  
      * @return true if using FTP passive mode
 382  
      */
 383  
     public boolean isPassive()
 384  
     {
 385  0
         return passive;
 386  
     }
 387  
 
 388  
     /**
 389  
      * Setter for FTP passive mode.
 390  
      * 
 391  
      * @param passive passive mode flag
 392  
      */
 393  
     public void setPassive(final boolean passive)
 394  
     {
 395  0
         this.passive = passive;
 396  0
     }
 397  
 
 398  
     /**
 399  
      * Passive mode is OFF by default. The value is taken from the connector
 400  
      * settings. In case there are any overriding properties set on the endpoint,
 401  
      * those will be used.
 402  
      * 
 403  
      * @see #setPassive(boolean)
 404  
      */
 405  
     public void enterActiveOrPassiveMode(FTPClient client, ImmutableEndpoint endpoint)
 406  
     {
 407  
         // well, no endpoint URI here, as we have to use the most common denominator
 408  
         // in API :(
 409  0
         final String passiveString = (String)endpoint.getProperty(FtpConnector.PROPERTY_PASSIVE_MODE);
 410  0
         if (passiveString == null)
 411  
         {
 412  
             // try the connector properties then
 413  0
             if (isPassive())
 414  
             {
 415  0
                 if (logger.isTraceEnabled())
 416  
                 {
 417  0
                     logger.trace("Entering FTP passive mode");
 418  
                 }
 419  0
                 client.enterLocalPassiveMode();
 420  
             }
 421  
             else
 422  
             {
 423  0
                 if (logger.isTraceEnabled())
 424  
                 {
 425  0
                     logger.trace("Entering FTP active mode");
 426  
                 }
 427  0
                 client.enterLocalActiveMode();
 428  
             }
 429  
         }
 430  
         else
 431  
         {
 432  
             // override with endpoint's definition
 433  0
             final boolean passiveMode = Boolean.valueOf(passiveString).booleanValue();
 434  0
             if (passiveMode)
 435  
             {
 436  0
                 if (logger.isTraceEnabled())
 437  
                 {
 438  0
                     logger.trace("Entering FTP passive mode (endpoint override)");
 439  
                 }
 440  0
                 client.enterLocalPassiveMode();
 441  
             }
 442  
             else
 443  
             {
 444  0
                 if (logger.isTraceEnabled())
 445  
                 {
 446  0
                     logger.trace("Entering FTP active mode (endpoint override)");
 447  
                 }
 448  0
                 client.enterLocalActiveMode();
 449  
             }
 450  
         }
 451  0
     }
 452  
 
 453  
     /**
 454  
      * Getter for FTP transfer type.
 455  
      * 
 456  
      * @return true if using FTP binary type
 457  
      */
 458  
     public boolean isBinary()
 459  
     {
 460  0
         return binary;
 461  
     }
 462  
 
 463  
     /**
 464  
      * Setter for FTP transfer type.
 465  
      * 
 466  
      * @param binary binary type flag
 467  
      */
 468  
     public void setBinary(final boolean binary)
 469  
     {
 470  0
         this.binary = binary;
 471  0
     }
 472  
 
 473  
     /**
 474  
      * Transfer type is BINARY by default. The value is taken from the connector
 475  
      * settings. In case there are any overriding properties set on the endpoint,
 476  
      * those will be used. <p/> The alternative type is ASCII. <p/>
 477  
      * 
 478  
      * @see #setBinary(boolean)
 479  
      */
 480  
     public void setupFileType(FTPClient client, ImmutableEndpoint endpoint) throws Exception
 481  
     {
 482  
         int type;
 483  
 
 484  
         // well, no endpoint URI here, as we have to use the most common denominator
 485  
         // in API :(
 486  0
         final String binaryTransferString = (String)endpoint.getProperty(FtpConnector.PROPERTY_BINARY_TRANSFER);
 487  0
         if (binaryTransferString == null)
 488  
         {
 489  
             // try the connector properties then
 490  0
             if (isBinary())
 491  
             {
 492  0
                 if (logger.isTraceEnabled())
 493  
                 {
 494  0
                     logger.trace("Using FTP BINARY type");
 495  
                 }
 496  0
                 type = org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE;
 497  
             }
 498  
             else
 499  
             {
 500  0
                 if (logger.isTraceEnabled())
 501  
                 {
 502  0
                     logger.trace("Using FTP ASCII type");
 503  
                 }
 504  0
                 type = org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE;
 505  
             }
 506  
         }
 507  
         else
 508  
         {
 509  
             // override with endpoint's definition
 510  0
             final boolean binaryTransfer = Boolean.valueOf(binaryTransferString).booleanValue();
 511  0
             if (binaryTransfer)
 512  
             {
 513  0
                 if (logger.isTraceEnabled())
 514  
                 {
 515  0
                     logger.trace("Using FTP BINARY type (endpoint override)");
 516  
                 }
 517  0
                 type = org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE;
 518  
             }
 519  
             else
 520  
             {
 521  0
                 if (logger.isTraceEnabled())
 522  
                 {
 523  0
                     logger.trace("Using FTP ASCII type (endpoint override)");
 524  
                 }
 525  0
                 type = org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE;
 526  
             }
 527  
         }
 528  
 
 529  0
         client.setFileType(type);
 530  0
     }
 531  
 
 532  
     /**
 533  
      * Well get the output stream (if any) for this type of transport. Typically this
 534  
      * will be called only when Streaming is being used on an outbound endpoint
 535  
      *
 536  
      * @param endpoint the endpoint that releates to this Dispatcher
 537  
      * @param message the current message being processed
 538  
      * @return the output stream to use for this request or null if the transport
 539  
      *         does not support streaming
 540  
      * @throws org.mule.api.MuleException
 541  
      */
 542  
     public OutputStream getOutputStream(OutboundEndpoint endpoint, MuleEvent event)
 543  
         throws MuleException
 544  
     {
 545  
         try
 546  
         {
 547  0
             final EndpointURI uri = endpoint.getEndpointURI();
 548  0
             String filename = getFilename(endpoint, event.getMessage());
 549  
 
 550  
             final FTPClient client;
 551  
             try
 552  
             {
 553  0
                 client = this.createFtpClient(endpoint);
 554  
             }
 555  0
             catch (Exception e)
 556  
             {
 557  0
                 throw new ConnectException(e, this);
 558  0
             }
 559  
             
 560  
             try
 561  
             {
 562  0
                 OutputStream out = client.storeFileStream(filename);
 563  0
                 if (out == null)
 564  
                 {
 565  0
                     throw new IOException("FTP operation failed: " + client.getReplyString());
 566  
                 }
 567  
                 
 568  0
                 return new CallbackOutputStream(out,
 569  
                         new CallbackOutputStream.Callback()
 570  0
                         {
 571  
                             public void onClose() throws Exception
 572  
                             {
 573  
                                 try
 574  
                                 {
 575  0
                                     if (!client.completePendingCommand())
 576  
                                     {
 577  0
                                         client.logout();
 578  0
                                         client.disconnect();
 579  0
                                         throw new IOException("FTP Stream failed to complete pending request");
 580  
                                     }
 581  
                                 }
 582  
                                 finally
 583  
                                 {
 584  0
                                     releaseFtp(uri, client);
 585  0
                                 }
 586  0
                             }
 587  
                         });
 588  
             }
 589  0
             catch (Exception e)
 590  
             {
 591  0
                 logger.debug("Error getting output stream: ", e);
 592  0
                 releaseFtp(uri, client);
 593  0
                 throw e;
 594  
             }
 595  
         }
 596  0
         catch (ConnectException ce)
 597  
         {
 598  
             // Don't wrap a ConnectException, otherwise the retry policy will not go into effect.
 599  0
             throw ce;
 600  
         }
 601  0
         catch (Exception e)
 602  
         {
 603  0
             throw new DispatchException(CoreMessages.streamingFailedNoStream(), event, endpoint, e);
 604  
         }
 605  
     }
 606  
 
 607  
     private String getFilename(ImmutableEndpoint endpoint, MuleMessage message) throws IOException
 608  
     {
 609  0
         String filename = message.getOutboundProperty(FtpConnector.PROPERTY_FILENAME);
 610  0
         String outPattern = (String) endpoint.getProperty(FtpConnector.PROPERTY_OUTPUT_PATTERN);
 611  0
         if (outPattern == null)
 612  
         {
 613  0
             outPattern = message.getOutboundProperty(FtpConnector.PROPERTY_OUTPUT_PATTERN, getOutputPattern());
 614  
         }
 615  0
         if (outPattern != null || filename == null)
 616  
         {
 617  0
             filename = generateFilename(message, outPattern);
 618  
         }
 619  0
         if (filename == null)
 620  
         {
 621  0
             throw new IOException("Filename is null");
 622  
         }
 623  0
         return filename;
 624  
     }
 625  
     
 626  
     private String generateFilename(MuleMessage message, String pattern)
 627  
     {
 628  0
         if (pattern == null)
 629  
         {
 630  0
             pattern = getOutputPattern();
 631  
         }
 632  0
         return getFilenameParser().getFilename(message, pattern);
 633  
     }
 634  
 
 635  
     /**
 636  
      * Creates a new FTPClient that logs in and changes the working directory using the data
 637  
      * provided in <code>endpoint</code>.
 638  
      */
 639  
     protected FTPClient createFtpClient(ImmutableEndpoint endpoint) throws Exception
 640  
     {
 641  0
         EndpointURI uri = endpoint.getEndpointURI();
 642  0
         FTPClient client = this.getFtp(uri);
 643  
 
 644  0
         this.enterActiveOrPassiveMode(client, endpoint);
 645  0
         this.setupFileType(client, endpoint);
 646  
 
 647  0
         String path = uri.getPath();
 648  
         // MULE-2400: if the path begins with '~' we must strip the first '/' to make things
 649  
         // work with FTPClient
 650  0
         if ((path.length() >= 2) && (path.charAt(1) == '~'))
 651  
         {
 652  0
             path = path.substring(1);
 653  
         }
 654  
         
 655  0
         if (!client.changeWorkingDirectory(path))
 656  
         {
 657  0
             throw new IOException(MessageFormat.format("Failed to change working directory to {0}. Ftp error: {1}",
 658  
                                                        path, client.getReplyCode()));
 659  
         }
 660  0
         return client;
 661  
     }
 662  
 
 663  
     /**
 664  
      * Override this method to do extra checking on the file.
 665  
      */
 666  
     protected boolean validateFile(FTPFile file)
 667  
     {
 668  0
         return true;
 669  
     }
 670  
 
 671  
     public boolean isStreaming()
 672  
     {
 673  0
         return streaming;
 674  
     }
 675  
 
 676  
     public void setStreaming(boolean streaming)
 677  
     {
 678  0
         this.streaming = streaming;
 679  0
     }
 680  
 
 681  
 }