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