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