001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.component.file.remote; 018 019 import java.io.ByteArrayOutputStream; 020 import java.io.File; 021 import java.io.FileOutputStream; 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.io.OutputStream; 025 import java.util.ArrayList; 026 import java.util.Arrays; 027 import java.util.Iterator; 028 import java.util.List; 029 030 import org.apache.camel.Exchange; 031 import org.apache.camel.InvalidPayloadException; 032 import org.apache.camel.component.file.FileComponent; 033 import org.apache.camel.component.file.GenericFile; 034 import org.apache.camel.component.file.GenericFileEndpoint; 035 import org.apache.camel.component.file.GenericFileExist; 036 import org.apache.camel.component.file.GenericFileOperationFailedException; 037 import org.apache.camel.util.FileUtil; 038 import org.apache.camel.util.IOHelper; 039 import org.apache.camel.util.ObjectHelper; 040 import org.apache.commons.net.ftp.FTPClient; 041 import org.apache.commons.net.ftp.FTPClientConfig; 042 import org.apache.commons.net.ftp.FTPFile; 043 import org.apache.commons.net.ftp.FTPReply; 044 import org.slf4j.Logger; 045 import org.slf4j.LoggerFactory; 046 047 /** 048 * FTP remote file operations 049 */ 050 public class FtpOperations implements RemoteFileOperations<FTPFile> { 051 052 protected final transient Logger log = LoggerFactory.getLogger(getClass()); 053 protected final FTPClient client; 054 protected final FTPClientConfig clientConfig; 055 protected RemoteFileEndpoint<FTPFile> endpoint; 056 057 public FtpOperations(FTPClient client, FTPClientConfig clientConfig) { 058 this.client = client; 059 this.clientConfig = clientConfig; 060 } 061 062 public void setEndpoint(GenericFileEndpoint<FTPFile> endpoint) { 063 this.endpoint = (RemoteFileEndpoint<FTPFile>) endpoint; 064 } 065 066 public boolean connect(RemoteFileConfiguration configuration) throws GenericFileOperationFailedException { 067 if (log.isTraceEnabled()) { 068 log.trace("Connecting using FTPClient: " + client); 069 } 070 071 String host = configuration.getHost(); 072 int port = configuration.getPort(); 073 String username = configuration.getUsername(); 074 075 if (clientConfig != null) { 076 log.trace("Configuring FTPClient with config: " + clientConfig); 077 client.configure(clientConfig); 078 } 079 080 if (log.isTraceEnabled()) { 081 log.trace("Connecting to " + configuration.remoteServerInformation() + " using connection timeout: " + client.getConnectTimeout()); 082 } 083 084 boolean connected = false; 085 int attempt = 0; 086 087 while (!connected) { 088 try { 089 if (log.isTraceEnabled() && attempt > 0) { 090 log.trace("Reconnect attempt #" + attempt + " connecting to + " + configuration.remoteServerInformation()); 091 } 092 client.connect(host, port); 093 // must check reply code if we are connected 094 int reply = client.getReplyCode(); 095 096 if (FTPReply.isPositiveCompletion(reply)) { 097 // yes we could connect 098 connected = true; 099 } else { 100 // throw an exception to force the retry logic in the catch exception block 101 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), "Server refused connection"); 102 } 103 } catch (Exception e) { 104 // check if we are interrupted so we can break out 105 if (Thread.currentThread().isInterrupted()) { 106 throw new GenericFileOperationFailedException("Interrupted during connecting", new InterruptedException("Interrupted during connecting")); 107 } 108 109 GenericFileOperationFailedException failed; 110 if (e instanceof GenericFileOperationFailedException) { 111 failed = (GenericFileOperationFailedException) e; 112 } else { 113 failed = new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 114 } 115 116 if (log.isTraceEnabled()) { 117 log.trace("Cannot connect due: " + failed.getMessage()); 118 } 119 attempt++; 120 if (attempt > endpoint.getMaximumReconnectAttempts()) { 121 throw failed; 122 } 123 if (endpoint.getReconnectDelay() > 0) { 124 try { 125 Thread.sleep(endpoint.getReconnectDelay()); 126 } catch (InterruptedException ie) { 127 // we could potentially also be interrupted during sleep 128 Thread.currentThread().interrupt(); 129 throw new GenericFileOperationFailedException("Interrupted during sleeping", ie); 130 } 131 } 132 } 133 } 134 135 // must enter passive mode directly after connect 136 if (configuration.isPassiveMode()) { 137 log.trace("Using passive mode connections"); 138 client.enterLocalPassiveMode(); 139 } 140 141 // must set soTimeout after connect 142 if (endpoint instanceof FtpEndpoint) { 143 FtpEndpoint ftpEndpoint = (FtpEndpoint) endpoint; 144 if (ftpEndpoint.getSoTimeout() > 0) { 145 log.trace("Using SoTimeout=" + ftpEndpoint.getSoTimeout()); 146 try { 147 client.setSoTimeout(ftpEndpoint.getSoTimeout()); 148 } catch (IOException e) { 149 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 150 } 151 } 152 } 153 154 try { 155 boolean login; 156 if (username != null) { 157 if (log.isTraceEnabled()) { 158 log.trace("Attempting to login user: " + username + " using password: " + configuration.getPassword()); 159 } 160 login = client.login(username, configuration.getPassword()); 161 } else { 162 log.trace("Attempting to login anonymous"); 163 login = client.login("anonymous", ""); 164 } 165 if (log.isTraceEnabled()) { 166 log.trace("User " + (username != null ? username : "anonymous") + " logged in: " + login); 167 } 168 if (!login) { 169 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString()); 170 } 171 client.setFileType(configuration.isBinary() ? FTPClient.BINARY_FILE_TYPE : FTPClient.ASCII_FILE_TYPE); 172 } catch (IOException e) { 173 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 174 } 175 176 // site commands 177 if (endpoint.getConfiguration().getSiteCommand() != null) { 178 // commands can be separated using new line 179 Iterator it = ObjectHelper.createIterator(endpoint.getConfiguration().getSiteCommand(), "\n"); 180 while (it.hasNext()) { 181 Object next = it.next(); 182 String command = endpoint.getCamelContext().getTypeConverter().convertTo(String.class, next); 183 if (log.isTraceEnabled()) { 184 log.trace("Site command to send: " + command); 185 } 186 if (command != null) { 187 boolean result = sendSiteCommand(command); 188 if (!result) { 189 throw new GenericFileOperationFailedException("Site command: " + command + " returned false"); 190 } 191 } 192 } 193 } 194 195 return true; 196 } 197 198 public boolean isConnected() throws GenericFileOperationFailedException { 199 return client.isConnected(); 200 } 201 202 public void disconnect() throws GenericFileOperationFailedException { 203 // logout before disconnecting 204 try { 205 client.logout(); 206 } catch (IOException e) { 207 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 208 } finally { 209 try { 210 client.disconnect(); 211 } catch (IOException e) { 212 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 213 } 214 } 215 } 216 217 public boolean deleteFile(String name) throws GenericFileOperationFailedException { 218 if (log.isDebugEnabled()) { 219 log.debug("Deleting file: " + name); 220 } 221 try { 222 return this.client.deleteFile(name); 223 } catch (IOException e) { 224 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 225 } 226 } 227 228 public boolean renameFile(String from, String to) throws GenericFileOperationFailedException { 229 if (log.isDebugEnabled()) { 230 log.debug("Renaming file: " + from + " to: " + to); 231 } 232 try { 233 return client.rename(from, to); 234 } catch (IOException e) { 235 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 236 } 237 } 238 239 public boolean buildDirectory(String directory, boolean absolute) throws GenericFileOperationFailedException { 240 // must normalize directory first 241 directory = endpoint.getConfiguration().normalizePath(directory); 242 243 if (log.isTraceEnabled()) { 244 log.trace("buildDirectory(" + directory + ")"); 245 } 246 try { 247 String originalDirectory = client.printWorkingDirectory(); 248 249 boolean success; 250 try { 251 // maybe the full directory already exists 252 success = client.changeWorkingDirectory(directory); 253 if (!success) { 254 if (log.isTraceEnabled()) { 255 log.trace("Trying to build remote directory: " + directory); 256 } 257 success = client.makeDirectory(directory); 258 if (!success) { 259 // we are here if the server side doesn't create intermediate folders so create the folder one by one 260 success = buildDirectoryChunks(directory); 261 } 262 } 263 264 return success; 265 } finally { 266 // change back to original directory 267 if (originalDirectory != null) { 268 changeCurrentDirectory(originalDirectory); 269 } 270 } 271 } catch (IOException e) { 272 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 273 } 274 } 275 276 public boolean retrieveFile(String name, Exchange exchange) throws GenericFileOperationFailedException { 277 if (log.isTraceEnabled()) { 278 log.trace("retrieveFile(" + name + ")"); 279 } 280 if (ObjectHelper.isNotEmpty(endpoint.getLocalWorkDirectory())) { 281 // local work directory is configured so we should store file content as files in this local directory 282 return retrieveFileToFileInLocalWorkDirectory(name, exchange); 283 } else { 284 // store file content directory as stream on the body 285 return retrieveFileToStreamInBody(name, exchange); 286 } 287 } 288 289 @SuppressWarnings("unchecked") 290 private boolean retrieveFileToStreamInBody(String name, Exchange exchange) throws GenericFileOperationFailedException { 291 OutputStream os = null; 292 boolean result; 293 try { 294 os = new ByteArrayOutputStream(); 295 GenericFile<FTPFile> target = (GenericFile<FTPFile>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE); 296 ObjectHelper.notNull(target, "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set"); 297 target.setBody(os); 298 299 String remoteName = name; 300 String currentDir = null; 301 if (endpoint.getConfiguration().isStepwise()) { 302 // remember current directory 303 currentDir = getCurrentDirectory(); 304 305 // change directory to path where the file is to be retrieved 306 // (must do this as some FTP servers cannot retrieve using absolute path) 307 String path = FileUtil.onlyPath(name); 308 if (path != null) { 309 changeCurrentDirectory(path); 310 } 311 // remote name is now only the file name as we just changed directory 312 remoteName = FileUtil.stripPath(name); 313 } 314 315 result = client.retrieveFile(remoteName, os); 316 317 // change back to current directory 318 if (endpoint.getConfiguration().isStepwise()) { 319 changeCurrentDirectory(currentDir); 320 } 321 322 } catch (IOException e) { 323 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 324 } finally { 325 IOHelper.close(os, "retrieve: " + name, log); 326 } 327 328 return result; 329 } 330 331 @SuppressWarnings("unchecked") 332 private boolean retrieveFileToFileInLocalWorkDirectory(String name, Exchange exchange) throws GenericFileOperationFailedException { 333 File temp; 334 File local = new File(FileUtil.normalizePath(endpoint.getLocalWorkDirectory())); 335 OutputStream os; 336 try { 337 // use relative filename in local work directory 338 GenericFile<FTPFile> target = (GenericFile<FTPFile>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE); 339 ObjectHelper.notNull(target, "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set"); 340 String relativeName = target.getRelativeFilePath(); 341 342 temp = new File(local, relativeName + ".inprogress"); 343 local = new File(local, relativeName); 344 345 // create directory to local work file 346 local.mkdirs(); 347 348 // delete any existing files 349 if (temp.exists()) { 350 if (!FileUtil.deleteFile(temp)) { 351 throw new GenericFileOperationFailedException("Cannot delete existing local work file: " + temp); 352 } 353 } 354 if (local.exists()) { 355 if (!FileUtil.deleteFile(local)) { 356 throw new GenericFileOperationFailedException("Cannot delete existing local work file: " + local); 357 } 358 } 359 360 // create new temp local work file 361 if (!temp.createNewFile()) { 362 throw new GenericFileOperationFailedException("Cannot create new local work file: " + temp); 363 } 364 365 // store content as a file in the local work directory in the temp handle 366 os = new FileOutputStream(temp); 367 368 // set header with the path to the local work file 369 exchange.getIn().setHeader(Exchange.FILE_LOCAL_WORK_PATH, local.getPath()); 370 371 } catch (Exception e) { 372 throw new GenericFileOperationFailedException("Cannot create new local work file: " + local); 373 } 374 375 boolean result; 376 try { 377 GenericFile<FTPFile> target = (GenericFile<FTPFile>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE); 378 // store the java.io.File handle as the body 379 target.setBody(local); 380 381 String remoteName = name; 382 String currentDir = null; 383 if (endpoint.getConfiguration().isStepwise()) { 384 // remember current directory 385 currentDir = getCurrentDirectory(); 386 387 // change directory to path where the file is to be retrieved 388 // (must do this as some FTP servers cannot retrieve using absolute path) 389 String path = FileUtil.onlyPath(name); 390 if (path != null) { 391 changeCurrentDirectory(path); 392 } 393 // remote name is now only the file name as we just changed directory 394 remoteName = FileUtil.stripPath(name); 395 } 396 397 result = client.retrieveFile(remoteName, os); 398 399 // change back to current directory 400 if (endpoint.getConfiguration().isStepwise()) { 401 changeCurrentDirectory(currentDir); 402 } 403 404 } catch (IOException e) { 405 if (log.isTraceEnabled()) { 406 log.trace("Error occurred during retrieving file: " + name + " to local directory. Deleting local work file: " + temp); 407 } 408 // failed to retrieve the file so we need to close streams and delete in progress file 409 // must close stream before deleting file 410 IOHelper.close(os, "retrieve: " + name, log); 411 boolean deleted = FileUtil.deleteFile(temp); 412 if (!deleted) { 413 log.warn("Error occurred during retrieving file: " + name + " to local directory. Cannot delete local work file: " + temp); 414 } 415 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 416 } finally { 417 // need to close the stream before rename it 418 IOHelper.close(os, "retrieve: " + name, log); 419 } 420 421 if (log.isDebugEnabled()) { 422 log.debug("Retrieve file to local work file result: " + result); 423 } 424 425 if (result) { 426 if (log.isTraceEnabled()) { 427 log.trace("Renaming local in progress file from: " + temp + " to: " + local); 428 } 429 // operation went okay so rename temp to local after we have retrieved the data 430 if (!FileUtil.renameFile(temp, local)) { 431 throw new GenericFileOperationFailedException("Cannot rename local work file from: " + temp + " to: " + local); 432 } 433 } 434 435 return result; 436 } 437 438 public boolean storeFile(String name, Exchange exchange) throws GenericFileOperationFailedException { 439 // must normalize name first 440 name = endpoint.getConfiguration().normalizePath(name); 441 442 if (log.isTraceEnabled()) { 443 log.trace("storeFile(" + name + ")"); 444 } 445 446 boolean answer = false; 447 String currentDir = null; 448 String path = FileUtil.onlyPath(name); 449 String targetName = name; 450 451 try { 452 if (path != null && endpoint.getConfiguration().isStepwise()) { 453 // must remember current dir so we stay in that directory after the write 454 currentDir = getCurrentDirectory(); 455 456 // change to path of name 457 changeCurrentDirectory(path); 458 459 // the target name should be without path, as we have changed directory 460 targetName = FileUtil.stripPath(name); 461 } 462 463 // store the file 464 answer = doStoreFile(name, targetName, exchange); 465 } finally { 466 // change back to current directory if we changed directory 467 if (currentDir != null) { 468 changeCurrentDirectory(currentDir); 469 } 470 } 471 472 return answer; 473 } 474 475 private boolean doStoreFile(String name, String targetName, Exchange exchange) throws GenericFileOperationFailedException { 476 if (log.isTraceEnabled()) { 477 log.trace("doStoreFile(" + targetName + ")"); 478 } 479 480 // if an existing file already exists what should we do? 481 if (endpoint.getFileExist() == GenericFileExist.Ignore || endpoint.getFileExist() == GenericFileExist.Fail) { 482 boolean existFile = existsFile(targetName); 483 if (existFile && endpoint.getFileExist() == GenericFileExist.Ignore) { 484 // ignore but indicate that the file was written 485 if (log.isTraceEnabled()) { 486 log.trace("An existing file already exists: " + name + ". Ignore and do not override it."); 487 } 488 return true; 489 } else if (existFile && endpoint.getFileExist() == GenericFileExist.Fail) { 490 throw new GenericFileOperationFailedException("File already exist: " + name + ". Cannot write new file."); 491 } 492 } 493 494 InputStream is = null; 495 try { 496 is = exchange.getIn().getMandatoryBody(InputStream.class); 497 if (endpoint.getFileExist() == GenericFileExist.Append) { 498 return client.appendFile(targetName, is); 499 } else { 500 return client.storeFile(targetName, is); 501 } 502 } catch (IOException e) { 503 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 504 } catch (InvalidPayloadException e) { 505 throw new GenericFileOperationFailedException("Cannot store file: " + name, e); 506 } finally { 507 IOHelper.close(is, "store: " + name, log); 508 } 509 } 510 511 public boolean existsFile(String name) throws GenericFileOperationFailedException { 512 if (log.isTraceEnabled()) { 513 log.trace("existsFile(" + name + ")"); 514 } 515 516 // check whether a file already exists 517 String directory = FileUtil.onlyPath(name); 518 String onlyName = FileUtil.stripPath(name); 519 try { 520 String[] names; 521 if (directory != null) { 522 names = client.listNames(directory); 523 } else { 524 names = client.listNames(); 525 } 526 // can return either null or an empty list depending on FTP servers 527 if (names == null) { 528 return false; 529 } 530 for (String existing : names) { 531 if (log.isTraceEnabled()) { 532 log.trace("Existing file: " + existing + ", target file: " + name); 533 } 534 existing = FileUtil.stripPath(existing); 535 if (existing != null && existing.equals(onlyName)) { 536 return true; 537 } 538 } 539 return false; 540 } catch (IOException e) { 541 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 542 } 543 } 544 545 public String getCurrentDirectory() throws GenericFileOperationFailedException { 546 if (log.isTraceEnabled()) { 547 log.trace("getCurrentDirectory()"); 548 } 549 try { 550 return client.printWorkingDirectory(); 551 } catch (IOException e) { 552 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 553 } 554 } 555 556 public void changeCurrentDirectory(String path) throws GenericFileOperationFailedException { 557 if (log.isTraceEnabled()) { 558 log.trace("changeCurrentDirectory(" + path + ")"); 559 } 560 if (ObjectHelper.isEmpty(path)) { 561 return; 562 } 563 564 // must compact path so FTP server can traverse correctly 565 path = FileUtil.compactPath(path); 566 567 // not stepwise should change directory in one operation 568 if (!endpoint.getConfiguration().isStepwise()) { 569 doChangeDirectory(path); 570 return; 571 } 572 573 // if it starts with the root path then a little special handling for that 574 if (FileUtil.hasLeadingSeparator(path)) { 575 // change to root path 576 doChangeDirectory(path.substring(0, 1)); 577 path = path.substring(1); 578 } 579 580 // split into multiple dirs 581 final String[] dirs = path.split("/|\\\\"); 582 583 if (dirs == null || dirs.length == 0) { 584 // path was just a relative single path 585 doChangeDirectory(path); 586 return; 587 } 588 589 // there are multiple dirs so do this in chunks 590 for (String dir : dirs) { 591 doChangeDirectory(dir); 592 } 593 } 594 595 private void doChangeDirectory(String path) { 596 if (path == null || ".".equals(path) || ObjectHelper.isEmpty(path)) { 597 return; 598 } 599 600 if (log.isTraceEnabled()) { 601 log.trace("Changing directory: " + path); 602 } 603 boolean success; 604 try { 605 if ("..".equals(path)) { 606 changeToParentDirectory(); 607 success = true; 608 } else { 609 success = client.changeWorkingDirectory(path); 610 } 611 } catch (IOException e) { 612 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 613 } 614 if (!success) { 615 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), "Cannot change directory to: " + path); 616 } 617 } 618 619 public void changeToParentDirectory() throws GenericFileOperationFailedException { 620 try { 621 client.changeToParentDirectory(); 622 } catch (IOException e) { 623 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 624 } 625 } 626 627 public List<FTPFile> listFiles() throws GenericFileOperationFailedException { 628 if (log.isTraceEnabled()) { 629 log.trace("listFiles()"); 630 } 631 try { 632 final List<FTPFile> list = new ArrayList<FTPFile>(); 633 FTPFile[] files = client.listFiles(); 634 // can return either null or an empty list depending on FTP servers 635 if (files != null) { 636 list.addAll(Arrays.asList(files)); 637 } 638 return list; 639 } catch (IOException e) { 640 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 641 } 642 } 643 644 public List<FTPFile> listFiles(String path) throws GenericFileOperationFailedException { 645 if (log.isTraceEnabled()) { 646 log.trace("listFiles(" + path + ")"); 647 } 648 // use current directory if path not given 649 if (ObjectHelper.isEmpty(path)) { 650 path = "."; 651 } 652 653 try { 654 final List<FTPFile> list = new ArrayList<FTPFile>(); 655 FTPFile[] files = client.listFiles(path); 656 // can return either null or an empty list depending on FTP servers 657 if (files != null) { 658 list.addAll(Arrays.asList(files)); 659 } 660 return list; 661 } catch (IOException e) { 662 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 663 } 664 } 665 666 public boolean sendNoop() throws GenericFileOperationFailedException { 667 if (log.isTraceEnabled()) { 668 log.trace("sendNoOp"); 669 } 670 try { 671 return client.sendNoOp(); 672 } catch (IOException e) { 673 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 674 } 675 } 676 677 public boolean sendSiteCommand(String command) throws GenericFileOperationFailedException { 678 if (log.isTraceEnabled()) { 679 log.trace("sendSiteCommand(" + command + ")"); 680 } 681 try { 682 return client.sendSiteCommand(command); 683 } catch (IOException e) { 684 throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); 685 } 686 } 687 688 protected FTPClient getFtpClient() { 689 return client; 690 } 691 692 private boolean buildDirectoryChunks(String dirName) throws IOException { 693 final StringBuilder sb = new StringBuilder(dirName.length()); 694 final String[] dirs = dirName.split("/|\\\\"); 695 696 boolean success = false; 697 for (String dir : dirs) { 698 sb.append(dir).append('/'); 699 // must normalize the directory name 700 String directory = endpoint.getConfiguration().normalizePath(sb.toString()); 701 702 // do not try to build root folder (/ or \) 703 if (!(directory.equals("/") || directory.equals("\\"))) { 704 if (log.isTraceEnabled()) { 705 log.trace("Trying to build remote directory by chunk: " + directory); 706 } 707 708 success = client.makeDirectory(directory); 709 } 710 } 711 712 return success; 713 } 714 715 }