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