11package org .hl7 .fhir .utilities ;
22
3+ import lombok .Getter ;
34import org .apache .commons .net .ftp .FTP ;
45import org .apache .commons .net .ftp .FTPReply ;
6+ import org .hl7 .fhir .exceptions .FHIRException ;
7+ import org .slf4j .Logger ;
8+ import org .slf4j .LoggerFactory ;
59
6-
7- import java .io .File ;
810import java .io .FileInputStream ;
911import java .io .IOException ;
1012
1113public class FTPClient {
1214
15+ private static final Logger logger = LoggerFactory .getLogger (FTPClient .class );
16+
1317 private final org .apache .commons .net .ftp .FTPClient clientImpl ;
1418
15- final String server ;
19+ @ Getter
20+ private final String server ;
21+
22+ @ Getter
23+ private final String path ;
1624
17- final String path ;
25+ private String resolvedPath = null ;
1826
19- final String user ;
27+ @ Getter
28+ private final String user ;
2029
21- final String password ;
30+ @ Getter
31+ private final String password ;
2232
23- final int port ;
33+ @ Getter
34+ private final int port ;
35+
36+ private final String remoteSeparator ;
2437
2538 /**
2639 * Connect to an FTP server
@@ -36,17 +49,26 @@ public FTPClient(String server, String path, String user, String password) {
3649 protected FTPClient (String server , int port , String path , String user , String password ) {
3750 this .server = server ;
3851 this .port = port ;
39- if (path .endsWith ("/" )) {
40- this .path = path ;
41- } else {
42- this .path = path + "/" ;
43- }
52+ this .remoteSeparator = "/" ;
53+ this .path = buildPath (path );
54+
4455 this .user = user ;
4556 this .password = password ;
4657
4758 clientImpl = new org .apache .commons .net .ftp .FTPClient ();
4859 }
4960
61+ private String buildPath (String path ) {
62+ if (path .length () == 0 ) {
63+ return "" ;
64+ }
65+ if (path .endsWith (remoteSeparator ))
66+ {
67+ return path ;
68+ }
69+ return path + remoteSeparator ;
70+ }
71+
5072 /**
5173 * Connect to the server, throw an exception if it fails
5274 */
@@ -60,13 +82,17 @@ public void connect() throws IOException {
6082
6183 clientImpl .login (user , password );
6284
85+ checkForPositiveCompletionAndLogErrors ("FTP server could not connect." , true );
6386
64- int reply = clientImpl .getReplyCode ( );
87+ logger . debug ( "Initial Working directory: " + clientImpl .printWorkingDirectory () );
6588
66- if (!FTPReply .isPositiveCompletion (reply )) {
67- clientImpl .disconnect ();
68- throw new IOException ("FTP server refused connection. Reply code: " + reply );
69- }
89+ clientImpl .changeWorkingDirectory (path );
90+
91+ checkForPositiveCompletionAndLogErrors ("FTP server could not establish default working directory" , true );
92+
93+ resolvedPath = clientImpl .printWorkingDirectory ();
94+
95+ logger .debug ("Resolved working directory: " + resolvedPath );
7096 }
7197
7298 /**
@@ -76,11 +102,53 @@ public void connect() throws IOException {
76102 */
77103 public void delete (String path ) throws IOException {
78104 String resolvedPath = resolveRemotePath (path );
105+ logger .debug ("Deleting remote file: " + resolvedPath );
79106 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 ;
80145 }
81146
82147 private String resolveRemotePath (String path ) {
83- return String .join ("" , this .path , 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 );
84152 }
85153
86154 /**
@@ -90,21 +158,59 @@ private String resolveRemotePath(String path) {
90158 */
91159 public void upload (String source , String path ) throws IOException {
92160 String resolvedPath = resolveRemotePath (path );
161+ logger .debug ("Uploading file to remote path: " + resolvedPath );
162+ createRemotePathIfNotExists (path );
163+
93164 FileInputStream localStream = new FileInputStream (source );
94165 clientImpl .setFileType (FTP .BINARY_FILE_TYPE );
95166 clientImpl .enterLocalPassiveMode ();
96167 clientImpl .storeFile ( resolvedPath , localStream );
97168 localStream .close ();
98169
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 {
99175 int reply = clientImpl .getReplyCode ();
100176
101- if (! FTPReply .isPositiveCompletion (reply )) {
102- throw new IOException ( "Error uploading file. Reply code: " + reply ) ;
177+ if ( FTPReply .isPositiveCompletion (reply )) {
178+ return ;
103179 }
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+
104188 }
105189
106190 public void disconnect () throws IOException {
107191 clientImpl .disconnect ();
108192 }
109193
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+
110216}
0 commit comments