View Javadoc

1   /*
2    * $Id: FtpMessageReceiver.java 7976 2007-08-21 14:26:13Z dirk.olmes $
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.providers.ftp;
12  
13  import org.mule.impl.MuleMessage;
14  import org.mule.providers.AbstractPollingMessageReceiver;
15  import org.mule.providers.file.FileConnector;
16  import org.mule.umo.UMOComponent;
17  import org.mule.umo.UMOMessage;
18  import org.mule.umo.endpoint.UMOEndpoint;
19  import org.mule.umo.endpoint.UMOEndpointURI;
20  import org.mule.umo.lifecycle.InitialisationException;
21  import org.mule.umo.provider.UMOConnector;
22  
23  import java.io.FilenameFilter;
24  import java.io.IOException;
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.io.output.ByteArrayOutputStream;
35  import org.apache.commons.net.ftp.FTPClient;
36  import org.apache.commons.net.ftp.FTPFile;
37  import org.apache.commons.net.ftp.FTPReply;
38  
39  public class FtpMessageReceiver extends AbstractPollingMessageReceiver
40  {
41      protected final FtpConnector connector;
42      protected final FilenameFilter filenameFilter;
43  
44      // there's nothing like homegrown pseudo-2PC.. :/
45      // shared state management like this should go into the connector and use
46      // something like commons-tx
47      protected final Set scheduledFiles = Collections.synchronizedSet(new HashSet());
48      protected final Set currentFiles = Collections.synchronizedSet(new HashSet());
49  
50      public FtpMessageReceiver(UMOConnector connector,
51                                UMOComponent component,
52                                UMOEndpoint endpoint,
53                                long frequency) throws InitialisationException
54      {
55          super(connector, component, endpoint);
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          final UMOEndpointURI uri = endpoint.getEndpointURI();
93          FTPClient client = connector.getFtp(uri);
94  
95          try
96          {
97              connector.enterActiveOrPassiveMode(client, endpoint);
98              connector.setupFileType(client, endpoint);
99  
100             final String path = uri.getPath();
101             if (!client.changeWorkingDirectory(path))
102             {
103                 throw new IOException(MessageFormat.format("Failed to change working directory to {0}. Ftp error: {1}",
104                                                            new Object[] {path, new Integer(client.getReplyCode())}));
105             }
106 
107             FTPFile[] files = client.listFiles();
108 
109             if (!FTPReply.isPositiveCompletion(client.getReplyCode()))
110             {
111                 throw new IOException("Failed to list files. Ftp error: " + client.getReplyCode());
112             }
113 
114             if (files == null || files.length == 0)
115             {
116                 return files;
117             }
118 
119             List v = new ArrayList();
120 
121             for (int i = 0; i < files.length; i++)
122             {
123                 if (files[i].isFile())
124                 {
125                     if (filenameFilter == null || filenameFilter.accept(null, files[i].getName()))
126                     {
127                         v.add(files[i]);
128                     }
129                 }
130             }
131 
132             return (FTPFile[]) v.toArray(new FTPFile[v.size()]);
133         }
134         finally
135         {
136             connector.releaseFtp(uri, client);
137         }
138     }
139 
140     protected void processFile(FTPFile file) throws Exception
141     {
142         logger.debug("entering processFile()");
143         UMOEndpointURI uri = endpoint.getEndpointURI();
144         FTPClient client = connector.getFtp(uri);
145 
146         try
147         {
148             connector.enterActiveOrPassiveMode(client, endpoint);
149             connector.setupFileType(client, endpoint);
150 
151             final String fileName = file.getName();
152             final String path = uri.getPath();
153             
154             if (!client.changeWorkingDirectory(path))
155             {
156                 throw new IOException(MessageFormat.format("Failed to change working directory to {0}. Ftp error: {1}",
157                                                            new Object[] {path, new Integer(client.getReplyCode())}));            }
158 
159             UMOMessage message;
160             if (endpoint.isStreaming())
161             {
162                 message = new MuleMessage(
163                         connector.getStreamMessageAdapter(client.retrieveFileStream(fileName), null));
164             }
165             else
166             {
167                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
168                 if (!client.retrieveFile(fileName, baos))
169                 {
170                     throw new IOException(MessageFormat.format("Failed to retrieve file {0}. Ftp error: {1}",
171                                                                new Object[] {fileName, new Integer(client.getReplyCode())}));
172                 }
173                 message = new MuleMessage(connector.getMessageAdapter(baos.toByteArray()));
174             }
175 
176             message.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, fileName);
177             routeMessage(message);
178 
179             if (!client.deleteFile(fileName))
180             {
181                 throw new IOException(MessageFormat.format("Failed to delete file {0}. Ftp error: {1}",
182                                                            new Object[] {fileName, new Integer(client.getReplyCode())}));
183             }
184         }
185         finally
186         {
187             logger.debug("leaving processFile()");
188             connector.releaseFtp(uri, client);
189         }
190     }
191 
192     protected void doConnect() throws Exception
193     {
194         // why?!
195         //connector.releaseFtp(getEndpointURI());
196     }
197 
198     protected void doDisconnect() throws Exception
199     {
200         // no op
201     }
202 
203     protected void doDispose()
204     {
205         // template method
206     }
207 
208     private final class FtpWork implements Work
209     {
210         private final String name;
211         private final FTPFile file;
212 
213         private FtpWork(String name, FTPFile file)
214         {
215             this.name = name;
216             this.file = file;
217         }
218 
219         public void run()
220         {
221             try
222             {
223                 currentFiles.add(name);
224                 processFile(file);
225             }
226             catch (Exception e)
227             {
228                 connector.handleException(e);
229             }
230             finally
231             {
232                 currentFiles.remove(name);
233                 scheduledFiles.remove(name);
234             }
235         }
236 
237         public void release()
238         {
239             // no op
240         }
241     }
242 
243 }