View Javadoc

1   /*
2    * $Id: FtpMessageReceiver.java 12377 2008-07-17 15:59:53Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.ftp;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.endpoint.InboundEndpoint;
16  import org.mule.api.lifecycle.CreateException;
17  import org.mule.api.service.Service;
18  import org.mule.api.transport.Connector;
19  import org.mule.transport.AbstractPollingMessageReceiver;
20  import org.mule.transport.file.FileConnector;
21  
22  import java.io.FilenameFilter;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.text.MessageFormat;
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Set;
31  
32  import javax.resource.spi.work.Work;
33  
34  import org.apache.commons.net.ftp.FTPClient;
35  import org.apache.commons.net.ftp.FTPFile;
36  import org.apache.commons.net.ftp.FTPReply;
37  
38  public class FtpMessageReceiver extends AbstractPollingMessageReceiver
39  {
40      protected final FtpConnector connector;
41      protected final FilenameFilter filenameFilter;
42  
43      // there's nothing like homegrown pseudo-2PC.. :/
44      // shared state management like this should go into the connector and use
45      // something like commons-tx
46      protected final Set scheduledFiles = Collections.synchronizedSet(new HashSet());
47      protected final Set currentFiles = Collections.synchronizedSet(new HashSet());
48  
49      public FtpMessageReceiver(Connector connector,
50                                Service service,
51                                InboundEndpoint endpoint,
52                                long frequency) throws CreateException
53      {
54          super(connector, service, endpoint);
55  
56          this.setFrequency(frequency);
57  
58          this.connector = (FtpConnector) connector;
59  
60          if (endpoint.getFilter() instanceof FilenameFilter)
61          {
62              this.filenameFilter = (FilenameFilter) endpoint.getFilter();
63          }
64          else
65          {
66              this.filenameFilter = null;
67          }
68      }
69  
70      public void poll() throws Exception
71      {
72          FTPFile[] files = listFiles();
73  
74          synchronized (scheduledFiles)
75          {
76              for (int i = 0; i < files.length; i++)
77              {
78                  final FTPFile file = files[i];
79                  final String fileName = file.getName();
80  
81                  if (!scheduledFiles.contains(fileName) && !currentFiles.contains(fileName))
82                  {
83                      scheduledFiles.add(fileName);
84                      getWorkManager().scheduleWork(new FtpWork(fileName, file));
85                  }
86              }
87          }
88      }
89  
90      protected FTPFile[] listFiles() throws Exception
91      {
92          FTPClient client = null;
93          try
94          {
95              client = connector.createFtpClient(endpoint);
96              FTPFile[] files = client.listFiles();
97  
98              if (!FTPReply.isPositiveCompletion(client.getReplyCode()))
99              {
100                 throw new IOException("Failed to list files. Ftp error: " + client.getReplyCode());
101             }
102 
103             if (files == null || files.length == 0)
104             {
105                 return files;
106             }
107 
108             List v = new ArrayList();
109 
110             for (int i = 0; i < files.length; i++)
111             {
112                 if (files[i].isFile())
113                 {
114                     if (filenameFilter == null || filenameFilter.accept(null, files[i].getName()))
115                     {
116                         v.add(files[i]);
117                     }
118                 }
119             }
120 
121             return (FTPFile[]) v.toArray(new FTPFile[v.size()]);
122         }
123         finally
124         {
125             if (client != null)
126             {
127                 connector.releaseFtp(endpoint.getEndpointURI(), client);
128             }
129         }
130     }
131 
132     protected void processFile(FTPFile file) throws Exception
133     {
134         logger.debug("entering processFile()");
135 
136         FTPClient client = null;
137         try
138         {
139             if (!connector.validateFile(file))
140             {
141                 return;
142             }
143             
144             client = connector.createFtpClient(endpoint);
145 
146             MuleMessage message;
147             InputStream stream = client.retrieveFileStream(file.getName());
148             if (stream == null)
149             {
150                 throw new IOException(MessageFormat.format("Failed to retrieve file {0}. Ftp error: {1}",
151                         new Object[]{file.getName(), new Integer(client.getReplyCode())}));
152             }
153             message = new DefaultMuleMessage(connector.getMessageAdapter(stream));
154             
155 
156             message.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, file.getName());
157             message.setProperty(FileConnector.PROPERTY_FILE_SIZE, new Long(file.getSize()));
158             routeMessage(message);
159 
160             postProcess(client, file, message);
161         }
162         finally
163         {
164             logger.debug("leaving processFile()");
165             if (client != null)
166             {
167                 connector.releaseFtp(endpoint.getEndpointURI(), client);
168             }
169         }
170     }
171 
172     protected void postProcess(FTPClient client, FTPFile file, MuleMessage message) throws Exception
173     {
174         if (!client.deleteFile(file.getName()))
175         {
176             throw new IOException(MessageFormat.format("Failed to delete file {0}. Ftp error: {1}",
177                     new Object[]{file.getName(), new Integer(client.getReplyCode())}));
178         }
179         logger.debug("Deleted processed file " + file.getName());
180     }
181     
182     protected void doConnect() throws Exception
183     {
184         // why?!
185         //connector.releaseFtp(getEndpointURI());
186     }
187 
188     protected void doDisconnect() throws Exception
189     {
190         // no op
191     }
192 
193     protected void doDispose()
194     {
195         // template method
196     }
197 
198     private final class FtpWork implements Work
199     {
200         private final String name;
201         private final FTPFile file;
202 
203         private FtpWork(String name, FTPFile file)
204         {
205             this.name = name;
206             this.file = file;
207         }
208 
209         public void run()
210         {
211             try
212             {
213                 currentFiles.add(name);
214                 processFile(file);
215             }
216             catch (Exception e)
217             {
218                 connector.handleException(e);
219             }
220             finally
221             {
222                 currentFiles.remove(name);
223                 scheduledFiles.remove(name);
224             }
225         }
226 
227         public void release()
228         {
229             // no op
230         }
231     }
232 
233 }