001package org.hl7.fhir.utilities; 002 003import lombok.Getter; 004import org.apache.commons.net.ftp.FTP; 005import org.apache.commons.net.ftp.FTPReply; 006import org.hl7.fhir.exceptions.FHIRException; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010import java.io.FileInputStream; 011import java.io.IOException; 012 013public class FTPClient { 014 015 private static final Logger logger = LoggerFactory.getLogger(FTPClient.class); 016 017 private final org.apache.commons.net.ftp.FTPClient clientImpl; 018 019 @Getter 020 private final String server; 021 022 @Getter 023 private final String path; 024 025 private String resolvedPath = null; 026 027 @Getter 028 private final String user; 029 030 @Getter 031 private final String password; 032 033 @Getter 034 private final int port; 035 036 private final String remoteSeparator; 037 038 /** 039 * Connect to an FTP server 040 * @param server - the server to connect to (usually just an IP address). It's up to the system to figure out access (VPN etc) 041 * @param path - the path on the FTP server to treat all the operations as relative to 042 * @param user - username for the FTP server 043 * @param password - password for the FTP server 044 */ 045 public FTPClient(String server, String path, String user, String password) { 046 this (server, -1, path, user, password); 047 } 048 049 protected FTPClient(String server, int port, String path, String user, String password) { 050 this.server = server; 051 this.port = port; 052 this.remoteSeparator = "/"; 053 this.path = buildPath(path); 054 055 this.user = user; 056 this.password = password; 057 058 clientImpl = new org.apache.commons.net.ftp.FTPClient(); 059 } 060 061 private String buildPath(String path) { 062 if (path.length() == 0) { 063 return ""; 064 } 065 if (path.endsWith(remoteSeparator)) 066 { 067 return path; 068 } 069 return path + remoteSeparator; 070 } 071 072 /** 073 * Connect to the server, throw an exception if it fails 074 */ 075 public void connect() throws IOException { 076 if (port != -1) { 077 clientImpl.connect(server, port); 078 } 079 else { 080 clientImpl.connect(server); 081 } 082 083 clientImpl.login(user, password); 084 085 checkForPositiveCompletionAndLogErrors("FTP server could not connect.", true); 086 087 logger.debug("Initial Working directory: " + clientImpl.printWorkingDirectory()); 088 089 clientImpl.changeWorkingDirectory(path); 090 091 checkForPositiveCompletionAndLogErrors("FTP server could not establish default working directory", true); 092 093 resolvedPath = clientImpl.printWorkingDirectory(); 094 095 logger.debug("Resolved working directory: " + resolvedPath); 096 } 097 098 /** 099 * Delete a file on the FTP server 100 * 101 * @param path - relative to the path provided in the constructor 102 */ 103 public void delete(String path) throws IOException { 104 String resolvedPath = resolveRemotePath(path); 105 logger.debug("Deleting remote file: " + resolvedPath); 106 clientImpl.deleteFile(resolvedPath); 107 checkForPositiveCompletionAndLogErrors("Error deleting file.", false); 108 logger.debug("Remote file deleted: " + resolvedPath); 109 } 110 111 /** 112 * Takes a file path and creates all intermediate directories if they do not yet exist. 113 * @param filePath relative to the path provided in the constructor and including the file name 114 * @throws IOException 115 */ 116 protected void createRemotePathIfNotExists(String filePath) throws IOException { 117 String[] subPath = filePath.split(remoteSeparator); 118 try { 119 for (int i = 0 ; i < subPath.length - 1; i++){ 120 if (subPath[i].isEmpty() ) { 121 continue; 122 } 123 boolean exists = clientImpl.changeWorkingDirectory(subPath[i]); 124 if (!exists) { 125 logger.debug("Remote directory does not exist: " + clientImpl.printWorkingDirectory() + remoteSeparator + subPath[i]); 126 clientImpl.makeDirectory(subPath[i]); 127 clientImpl.changeWorkingDirectory(subPath[i]); 128 logger.debug("Made remote directory: " + clientImpl.printWorkingDirectory()); 129 } 130 }} catch (IOException e) { 131 throw new IOException("Error creating remote path: " + filePath, e); 132 } finally { 133 clientImpl.changeWorkingDirectory(this.resolvedPath); 134 } 135 } 136 137 protected boolean remotePathExists(String path) throws IOException { 138 boolean output; 139 try { 140 output = clientImpl.changeWorkingDirectory(path); 141 } finally { 142 clientImpl.changeWorkingDirectory(this.resolvedPath); 143 } 144 return output; 145 } 146 147 private String resolveRemotePath(String path) { 148 if (path.startsWith(remoteSeparator)) { 149 throw new IllegalArgumentException("Absolute remote path is not permitted. Path: " + path); 150 } 151 return String.join(remoteSeparator, path); 152 } 153 154 /** 155 * Upload a file from the local system to the FTP Server 156 * @param source - absolute path on local system 157 * @param path - relative to the path provided in the constructor 158 */ 159 public void upload(String source, String path) throws IOException { 160 String resolvedPath = resolveRemotePath(path); 161 logger.debug("Uploading file to remote path: " + resolvedPath); 162 createRemotePathIfNotExists(path); 163 164 FileInputStream localStream = new FileInputStream(source); 165 clientImpl.setFileType(FTP.BINARY_FILE_TYPE); 166 clientImpl.enterLocalPassiveMode(); 167 clientImpl.storeFile( resolvedPath, localStream); 168 localStream.close(); 169 170 checkForPositiveCompletionAndLogErrors("Error uploading file.", false); 171 logger.debug("Remote file uploaded: " + resolvedPath); 172 } 173 174 private void checkForPositiveCompletionAndLogErrors(String localErrorMessage, boolean disconnectOnError) throws IOException { 175 int reply = clientImpl.getReplyCode(); 176 177 if (FTPReply.isPositiveCompletion(reply)) { 178 return; 179 } 180 181 String remoteErrorMessage = clientImpl.getReplyString(); 182 if (disconnectOnError) { 183 clientImpl.disconnect(); 184 } 185 throw new IOException(localErrorMessage + " Reply code: " + reply + " Message: " + remoteErrorMessage); 186 187 188 } 189 190 public void disconnect() throws IOException { 191 clientImpl.disconnect(); 192 } 193 194 195 public static void main(String[] args) throws IOException, FHIRException { 196 FTPClient ftp = new FTPClient(getNamedParam(args, "-upload-server"), getNamedParam(args, "-upload-path"), getNamedParam(args, "-upload-user"), getNamedParam(args, "-upload-password")); 197 ftp.connect(); 198 ftp.upload("/Users/grahamegrieve/temp/test.xml", "testing/test.xml"); 199 ftp.delete("testing/test.xml"); 200 ftp.disconnect(); 201 } 202 203 private static String getNamedParam(String[] args, String param) { 204 boolean found = false; 205 for (String a : args) { 206 if (found) 207 return a; 208 if (a.equals(param)) { 209 found = true; 210 } 211 } 212 return null; 213 } 214 215 216}