View Javadoc

1   /*
2    * $Id: SftpUtil.java 21125 2011-01-26 21:21:10Z dzapata $
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.sftp;
12  
13  import java.io.File;
14  import java.io.FileOutputStream;
15  import java.io.IOException;
16  import java.io.InputStream;
17  import java.text.SimpleDateFormat;
18  import java.util.Date;
19  
20  import org.apache.commons.io.IOUtils;
21  import org.apache.log4j.Logger;
22  import org.mule.api.endpoint.ImmutableEndpoint;
23  
24  /**
25   * Contains reusable methods not directly related to usage of the jsch sftp library
26   * (they can be found in the class SftpClient).
27   * 
28   * @author Magnus Larsson
29   */
30  public class SftpUtil
31  {
32      /** Logger */
33      private static final Logger logger = Logger.getLogger(SftpUtil.class);
34  
35      private SftpConnector connector;
36      private ImmutableEndpoint endpoint;
37  
38      private static final String DUPLICATE_HANDLING_DEFAULT = SftpConnector.PROPERTY_DUPLICATE_HANDLING_THROW_EXCEPTION;
39      private static final boolean KEEP_FILE_ON_ERROR_DEFAULT = true;
40      private static final boolean USE_TEMP_FILE_TIMESTAMP_SUFFIX_DEFAULT = false;
41      private static final long SIZE_CHECK_WAIT_TIME_DEFAULT = -1;
42  
43      private final static Object lock = new Object();
44  
45      public SftpUtil(ImmutableEndpoint endpoint)
46      {
47          this.endpoint = endpoint;
48          this.connector = (SftpConnector) endpoint.getConnector();
49      }
50  
51      public String createUniqueSuffix(String filename)
52      {
53  
54          // TODO. Add code for handling no '.'
55          int fileTypeIdx = filename.lastIndexOf('.');
56          String fileType = filename.substring(fileTypeIdx); // Let the fileType
57                                                             // include the leading '.'
58  
59          filename = filename.substring(0, fileTypeIdx); // Strip off the leading '/'
60                                                         // from the filename
61  
62          SimpleDateFormat timestampFormatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
63          String timstampStr = '_' + timestampFormatter.format(new Date());
64  
65          return filename + timstampStr + fileType;
66      }
67  
68      public String getTempDirInbound()
69      {
70          String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_TEMP_DIR);
71          if (endpointValue != null)
72          {
73              return endpointValue;
74          }
75  
76          String connectorValue = connector.getTempDirInbound();
77          if (connectorValue != null)
78          {
79              return connectorValue;
80          }
81  
82          return null;
83      }
84  
85      public boolean isUseTempDirInbound()
86      {
87          return getTempDirInbound() != null;
88      }
89  
90      public String getTempDirOutbound()
91      {
92          String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_TEMP_DIR);
93          if (endpointValue != null)
94          {
95              return endpointValue;
96          }
97  
98          String connectorValue = connector.getTempDirOutbound();
99          if (connectorValue != null)
100         {
101             return connectorValue;
102         }
103 
104         return null;
105     }
106 
107     public boolean isUseTempDirOutbound()
108     {
109         return getTempDirOutbound() != null;
110     }
111 
112     public void cleanupTempDir(SftpClient sftpClient, String transferFileName, String tempDir)
113     {
114         String tempDirAbs = sftpClient.getAbsolutePath(endpoint.getEndpointURI().getPath() + "/" + tempDir);
115         try
116         {
117             sftpClient.changeWorkingDirectory(tempDirAbs);
118             sftpClient.deleteFile(transferFileName);
119         }
120         catch (Exception e)
121         {
122             logger.error("Could not delete the file '" + transferFileName + "' from the temp directory '"
123                          + tempDirAbs + "'", e);
124         }
125     }
126 
127     public long getSizeCheckWaitTime()
128     {
129         Object endpointValue = endpoint.getProperty(SftpConnector.PROPERTY_SIZE_CHECK_WAIT_TIME);
130         if (endpointValue != null)
131         {
132             return Long.valueOf((String) endpointValue);
133         }
134 
135         Long connectorValue = connector.getSizeCheckWaitTime();
136         if (connectorValue != null)
137         {
138             return connectorValue;
139         }
140 
141         return SIZE_CHECK_WAIT_TIME_DEFAULT;
142     }
143 
144     public String getArchiveDir()
145     {
146         String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_ARCHIVE_DIR);
147         if (endpointValue != null)
148         {
149             return endpointValue;
150         }
151 
152         String connectorValue = connector.getArchiveDir();
153         if (connectorValue != null)
154         {
155             return connectorValue;
156         }
157 
158         return null;
159     }
160 
161     public String getArchiveTempReceivingDir()
162     {
163         String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_ARCHIVE_TEMP_RECEIVING_DIR);
164         if (endpointValue != null)
165         {
166             return endpointValue;
167         }
168 
169         String connectorValue = connector.getArchiveTempReceivingDir();
170         if (connectorValue != null)
171         {
172             return connectorValue;
173         }
174 
175         return null;
176     }
177 
178     public String getArchiveTempSendingDir()
179     {
180         String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_ARCHIVE_TEMP_SENDING_DIR);
181         if (endpointValue != null)
182         {
183             return endpointValue;
184         }
185 
186         String connectorValue = connector.getArchiveTempSendingDir();
187         if (connectorValue != null)
188         {
189             return connectorValue;
190         }
191 
192         return null;
193     }
194 
195     public boolean isUseTempFileTimestampSuffix()
196     {
197         Object endpointValue = endpoint.getProperty(SftpConnector.PROPERTY_USE_TEMP_FILE_TIMESTAMP_SUFFIX);
198         if (endpointValue != null)
199         {
200             return Boolean.valueOf((String) endpointValue);
201         }
202 
203         Boolean connectorValue = connector.isUseTempFileTimestampSuffix();
204         if (connectorValue != null)
205         {
206             return connectorValue;
207         }
208 
209         return USE_TEMP_FILE_TIMESTAMP_SUFFIX_DEFAULT;
210     }
211 
212     public String getDuplicateHandling()
213     {
214         String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_DUPLICATE_HANDLING);
215         if (endpointValue != null)
216         {
217             return endpointValue;
218         }
219 
220         String connectorValue = connector.getDuplicateHandling();
221         if (connectorValue != null)
222         {
223             return connectorValue;
224         }
225 
226         return DUPLICATE_HANDLING_DEFAULT;
227     }
228 
229     public String getIdentityFile()
230     {
231         String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_IDENTITY_FILE);
232         if (endpointValue != null)
233         {
234             return endpointValue;
235         }
236 
237         String connectorValue = connector.getIdentityFile();
238         if (connectorValue != null)
239         {
240             return connectorValue;
241         }
242 
243         return null;
244     }
245 
246     public String getPassphrase()
247     {
248         String endpointValue = (String) endpoint.getProperty(SftpConnector.PROPERTY_PASS_PHRASE);
249         if (endpointValue != null)
250         {
251             return endpointValue;
252         }
253 
254         String connectorValue = connector.getPassphrase();
255         if (connectorValue != null)
256         {
257             return connectorValue;
258         }
259 
260         return null;
261     }
262 
263     /**
264      * Changes the directory to the temp-dir on the <b>outbound</b> endpoint. Will
265      * create the directory if it not already exists.
266      * <p/>
267      * Note, this method is synchronized because it in rare cases can be called from
268      * two threads at the same time and thus cause an error.
269      * 
270      * @param sftpClient
271      * @param endpointDir
272      * @throws IOException
273      */
274     public void cwdToTempDirOnOutbound(SftpClient sftpClient, String endpointDir) throws IOException
275     {
276         String tempDir = getTempDirOutbound();
277         String tempDirAbs = sftpClient.getAbsolutePath(endpointDir + "/" + tempDir);
278 
279         // We need to have a synchronized block if two++ threads tries to
280         // create the same directory at the same time
281         synchronized (lock)
282         {
283             // Try to change directory to the temp dir, if it fails - create it
284             try
285             {
286                 // This method will throw an exception if the directory does not
287                 // exist.
288                 sftpClient.changeWorkingDirectory(tempDirAbs);
289             }
290             catch (IOException e)
291             {
292                 logger.info("Got an exception when trying to change the working directory to the temp dir. "
293                             + "Will try to create the directory " + tempDirAbs);
294                 sftpClient.changeWorkingDirectory(endpointDir);
295                 sftpClient.mkdir(tempDir);
296                 // Now it should exist!
297                 sftpClient.changeWorkingDirectory(tempDirAbs);
298             }
299         }
300     }
301 
302     public boolean isKeepFileOnError()
303     {
304         Object endpointValue = endpoint.getProperty(SftpConnector.PROPERTY_KEEP_FILE_ON_ERROR);
305         if (endpointValue != null)
306         {
307             return Boolean.valueOf((String) endpointValue);
308         }
309 
310         Boolean connectorValue = connector.isKeepFileOnError();
311         if (connectorValue != null)
312         {
313             return connectorValue;
314         }
315 
316         return KEEP_FILE_ON_ERROR_DEFAULT;
317     }
318 
319     /**
320      * Should be moved to a util class that is not based on an endpoint... TODO: why
321      * is this method synchronized?
322      * 
323      * @param input
324      * @param destination
325      * @throws IOException
326      */
327     public synchronized void copyStreamToFile(InputStream input, File destination) throws IOException
328     {
329         try
330         {
331             File folder = destination.getParentFile();
332             if (!folder.exists())
333             {
334                 throw new IOException("Destination folder does not exist: " + folder);
335             }
336 
337             if (!folder.canWrite())
338             {
339                 throw new IOException("Destination folder is not writeable: " + folder);
340             }
341 
342             FileOutputStream output = new FileOutputStream(destination);
343             try
344             {
345                 IOUtils.copy(input, output);
346             }
347             finally
348             {
349                 if (output != null) output.close();
350             }
351         }
352         catch (IOException ex)
353         {
354             setErrorOccurredOnInputStream(input);
355             throw ex;
356         }
357         catch (RuntimeException ex)
358         {
359             setErrorOccurredOnInputStream(input);
360             throw ex;
361         }
362         finally
363         {
364             if (input != null) input.close();
365         }
366     }
367 
368     public void setErrorOccurredOnInputStream(InputStream inputStream)
369     {
370 
371         if (isKeepFileOnError())
372         {
373             // If an exception occurs and the keepFileOnError property is
374             // true, keep the file on the originating endpoint
375             // Note: this is only supported when using the sftp transport on
376             // both inbound & outbound
377             if (inputStream != null)
378             {
379                 if (inputStream instanceof ErrorOccurredDecorator)
380                 {
381                     // Ensure that the SftpInputStream or
382                     // SftpFileArchiveInputStream knows about the error and
383                     // dont delete the file
384                     ((ErrorOccurredDecorator) inputStream).setErrorOccurred();
385 
386                 }
387                 else
388                 {
389                     logger.warn("Class "
390                                 + inputStream.getClass().getName()
391                                 + " did not implement the 'ErrorOccurred' decorator, errorOccured=true could not be set.");
392                 }
393             }
394         }
395     }
396 }