1
2
3
4
5
6
7 package org.mule.transport.ftp;
8
9 import org.mule.api.MuleMessage;
10 import org.mule.api.construct.FlowConstruct;
11 import org.mule.api.endpoint.InboundEndpoint;
12 import org.mule.api.lifecycle.CreateException;
13 import org.mule.api.lifecycle.InitialisationException;
14 import org.mule.api.retry.RetryContext;
15 import org.mule.api.transport.Connector;
16 import org.mule.transport.AbstractPollingMessageReceiver;
17 import org.mule.transport.ConnectException;
18
19 import java.io.FilenameFilter;
20 import java.io.IOException;
21 import java.text.MessageFormat;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Set;
27
28 import javax.resource.spi.work.Work;
29
30 import org.apache.commons.net.ftp.FTPClient;
31 import org.apache.commons.net.ftp.FTPFile;
32 import org.apache.commons.net.ftp.FTPListParseEngine;
33 import org.apache.commons.net.ftp.FTPReply;
34
35 public class FtpMessageReceiver extends AbstractPollingMessageReceiver
36 {
37 private final static int FTP_LIST_PAGE_SIZE = 25;
38 protected final FtpConnector connector;
39 protected final FilenameFilter filenameFilter;
40
41
42
43
44 protected final Set<String> scheduledFiles = Collections.synchronizedSet(new HashSet<String>());
45 protected final Set<String> currentFiles = Collections.synchronizedSet(new HashSet<String>());
46
47 public FtpMessageReceiver(Connector connector,
48 FlowConstruct flowConstruct,
49 InboundEndpoint endpoint,
50 long frequency) throws CreateException
51 {
52 super(connector, flowConstruct, endpoint);
53
54 this.setFrequency(frequency);
55
56 this.connector = (FtpConnector) connector;
57
58 if (endpoint.getFilter() instanceof FilenameFilter)
59 {
60 this.filenameFilter = (FilenameFilter) endpoint.getFilter();
61 }
62 else
63 {
64 this.filenameFilter = null;
65 }
66 }
67
68 @Override
69 public void poll() throws Exception
70 {
71 FTPFile[] files = listFiles();
72 if (logger.isDebugEnabled())
73 {
74 logger.debug("Poll encountered " + files.length + " new file(s)");
75 }
76
77 synchronized (scheduledFiles)
78 {
79 for (final FTPFile file : files)
80 {
81 if (getLifecycleState().isStopping())
82 {
83 break;
84 }
85
86 final String fileName = file.getName();
87
88 if (!scheduledFiles.contains(fileName) && !currentFiles.contains(fileName))
89 {
90 scheduledFiles.add(fileName);
91 getWorkManager().scheduleWork(new FtpWork(fileName, file));
92 }
93 }
94 }
95 }
96
97 protected FTPFile[] listFiles() throws Exception
98 {
99 FTPClient client = null;
100 try
101 {
102 try
103 {
104 client = connector.createFtpClient(endpoint);
105 }
106 catch (Exception e)
107 {
108 throw new ConnectException(e, this);
109 }
110 FTPListParseEngine engine = client.initiateListParsing();
111 FTPFile[] files = null;
112 List<FTPFile> v = new ArrayList<FTPFile>();
113 while (engine.hasNext())
114 {
115 if (getLifecycleState().isStopping())
116 {
117 break;
118 }
119 files = engine.getNext(FTP_LIST_PAGE_SIZE);
120 if (files == null || files.length == 0)
121 {
122 return files;
123 }
124 for (FTPFile file : files)
125 {
126 if (file.isFile())
127 {
128 if (filenameFilter == null || filenameFilter.accept(null, file.getName()))
129 {
130 v.add(file);
131 }
132 }
133 }
134 }
135
136 if (!FTPReply.isPositiveCompletion(client.getReplyCode()))
137 {
138 throw new IOException("Failed to list files. Ftp error: " + client.getReplyCode());
139 }
140
141 return v.toArray(new FTPFile[v.size()]);
142 }
143 finally
144 {
145 if (client != null)
146 {
147 connector.releaseFtp(endpoint.getEndpointURI(), client);
148 }
149 }
150 }
151
152 protected void processFile(FTPFile file) throws Exception
153 {
154 FTPClient client = null;
155 try
156 {
157 if (!connector.validateFile(file))
158 {
159 return;
160 }
161
162 try
163 {
164 client = connector.createFtpClient(endpoint);
165 }
166 catch (Exception e)
167 {
168 throw new ConnectException(e, this);
169 }
170
171 FtpMuleMessageFactory muleMessageFactory = createMuleMessageFactory(client);
172 MuleMessage message = muleMessageFactory.create(file, endpoint.getEncoding());
173
174 routeMessage(message);
175 postProcess(client, file, message);
176 }
177 finally
178 {
179 if (client != null)
180 {
181 connector.releaseFtp(endpoint.getEndpointURI(), client);
182 }
183 }
184 }
185
186 @Override
187 protected void initializeMessageFactory() throws InitialisationException
188 {
189
190
191
192 }
193
194 protected FtpMuleMessageFactory createMuleMessageFactory(FTPClient client) throws CreateException
195 {
196 FtpMuleMessageFactory factory = (FtpMuleMessageFactory) createMuleMessageFactory();
197 factory.setStreaming(connector.isStreaming());
198 factory.setFtpClient(client);
199
200 return factory;
201 }
202
203 protected void postProcess(FTPClient client, FTPFile file, MuleMessage message) throws Exception
204 {
205 if (!client.deleteFile(file.getName()))
206 {
207 throw new IOException(MessageFormat.format("Failed to delete file {0}. Ftp error: {1}",
208 file.getName(), client.getReplyCode()));
209 }
210 if (logger.isDebugEnabled())
211 {
212 logger.debug("Deleted processed file " + file.getName());
213 }
214
215 if (connector.isStreaming())
216 {
217 if (!client.completePendingCommand())
218 {
219 throw new IOException(MessageFormat.format("Failed to complete a pending command. Retrieveing file {0}. Ftp error: {1}",
220 file.getName(), client.getReplyCode()));
221 }
222 }
223 }
224
225 @Override
226 protected void doConnect() throws Exception
227 {
228
229 }
230
231 @Override
232 public RetryContext validateConnection(RetryContext retryContext)
233 {
234 FTPClient client = null;
235 try
236 {
237 client = connector.createFtpClient(endpoint);
238 client.sendNoOp();
239 client.logout();
240 client.disconnect();
241
242 retryContext.setOk();
243 }
244 catch (Exception ex)
245 {
246 retryContext.setFailed(ex);
247 }
248 finally
249 {
250 try
251 {
252 if (client != null)
253 {
254 connector.releaseFtp(endpoint.getEndpointURI(), client);
255 }
256 }
257 catch (Exception e)
258 {
259 if (logger.isDebugEnabled())
260 {
261 logger.debug("Failed to release ftp client " + client, e);
262 }
263 }
264 }
265
266 return retryContext;
267 }
268
269 @Override
270 protected void doDisconnect() throws Exception
271 {
272
273 }
274
275 @Override
276 protected void doDispose()
277 {
278
279 }
280
281 private final class FtpWork implements Work
282 {
283 private final String name;
284 private final FTPFile file;
285
286 private FtpWork(String name, FTPFile file)
287 {
288 this.name = name;
289 this.file = file;
290 }
291
292 public void run()
293 {
294 try
295 {
296 currentFiles.add(name);
297 processFile(file);
298 }
299 catch (Exception e)
300 {
301 getConnector().getMuleContext().getExceptionListener().handleException(e);
302 }
303 finally
304 {
305 currentFiles.remove(name);
306 scheduledFiles.remove(name);
307 }
308 }
309
310 public void release()
311 {
312
313 }
314 }
315
316 }