1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
package org.mule.transport.file; |
12 | |
|
13 | |
import org.mule.DefaultMuleMessage; |
14 | |
import org.mule.api.DefaultMuleException; |
15 | |
import org.mule.api.MuleException; |
16 | |
import org.mule.api.endpoint.InboundEndpoint; |
17 | |
import org.mule.api.lifecycle.CreateException; |
18 | |
import org.mule.api.routing.RoutingException; |
19 | |
import org.mule.api.service.Service; |
20 | |
import org.mule.api.transport.Connector; |
21 | |
import org.mule.api.transport.MessageAdapter; |
22 | |
import org.mule.transport.AbstractPollingMessageReceiver; |
23 | |
import org.mule.transport.ConnectException; |
24 | |
import org.mule.transport.DefaultMessageAdapter; |
25 | |
import org.mule.transport.file.i18n.FileMessages; |
26 | |
import org.mule.util.FileUtils; |
27 | |
|
28 | |
import java.io.File; |
29 | |
import java.io.FileFilter; |
30 | |
import java.io.FileNotFoundException; |
31 | |
import java.io.FilenameFilter; |
32 | |
import java.io.IOException; |
33 | |
import java.io.RandomAccessFile; |
34 | |
import java.nio.channels.FileChannel; |
35 | |
import java.nio.channels.FileLock; |
36 | |
import java.util.Comparator; |
37 | |
|
38 | |
import edu.emory.mathcs.backport.java.util.Arrays; |
39 | |
|
40 | |
import org.apache.commons.collections.comparators.ReverseComparator; |
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
public class FileMessageReceiver extends AbstractPollingMessageReceiver |
48 | |
{ |
49 | |
public static final String COMPARATOR_CLASS_NAME_PROPERTY = "comparator"; |
50 | |
public static final String COMPARATOR_REVERSE_ORDER_PROPERTY = "reverseOrder"; |
51 | |
|
52 | 28 | private String readDir = null; |
53 | 28 | private String moveDir = null; |
54 | 28 | private File readDirectory = null; |
55 | 28 | private File moveDirectory = null; |
56 | 28 | private String moveToPattern = null; |
57 | 28 | private FilenameFilter filenameFilter = null; |
58 | 28 | private FileFilter fileFilter = null; |
59 | |
|
60 | |
public FileMessageReceiver(Connector connector, |
61 | |
Service service, |
62 | |
InboundEndpoint endpoint, |
63 | |
String readDir, |
64 | |
String moveDir, |
65 | |
String moveToPattern, |
66 | |
long frequency) throws CreateException |
67 | |
{ |
68 | 28 | super(connector, service, endpoint); |
69 | 28 | this.setFrequency(frequency); |
70 | |
|
71 | 28 | this.readDir = readDir; |
72 | 28 | this.moveDir = moveDir; |
73 | 28 | this.moveToPattern = moveToPattern; |
74 | |
|
75 | 28 | if (endpoint.getFilter() instanceof FilenameFilter) |
76 | |
{ |
77 | 0 | filenameFilter = (FilenameFilter) endpoint.getFilter(); |
78 | |
} |
79 | 28 | else if (endpoint.getFilter() instanceof FileFilter) |
80 | |
{ |
81 | 0 | fileFilter = (FileFilter) endpoint.getFilter(); |
82 | |
} |
83 | 28 | else if (endpoint.getFilter() != null) |
84 | |
{ |
85 | 0 | throw new CreateException(FileMessages.invalidFileFilter(endpoint.getEndpointURI()), this); |
86 | |
} |
87 | 28 | } |
88 | |
|
89 | |
protected void doConnect() throws Exception |
90 | |
{ |
91 | 22 | if (readDir != null) |
92 | |
{ |
93 | 22 | readDirectory = FileUtils.openDirectory(readDir); |
94 | 22 | if (!(readDirectory.canRead())) |
95 | |
{ |
96 | 0 | throw new ConnectException(FileMessages.fileDoesNotExist(readDirectory.getAbsolutePath()), this); |
97 | |
} |
98 | |
else |
99 | |
{ |
100 | 22 | logger.debug("Listening on endpointUri: " + readDirectory.getAbsolutePath()); |
101 | |
} |
102 | |
} |
103 | |
|
104 | 22 | if (moveDir != null) |
105 | |
{ |
106 | 8 | moveDirectory = FileUtils.openDirectory((moveDir)); |
107 | 8 | if (!(moveDirectory.canRead()) || !moveDirectory.canWrite()) |
108 | |
{ |
109 | 0 | throw new ConnectException(FileMessages.moveToDirectoryNotWritable(), this); |
110 | |
} |
111 | |
} |
112 | 22 | } |
113 | |
|
114 | |
protected void doDisconnect() throws Exception |
115 | |
{ |
116 | |
|
117 | 22 | } |
118 | |
|
119 | |
protected void doDispose() |
120 | |
{ |
121 | |
|
122 | 46 | } |
123 | |
|
124 | |
public void poll() |
125 | |
{ |
126 | |
try |
127 | |
{ |
128 | 35 | File[] files = this.listFiles(); |
129 | 35 | if (logger.isDebugEnabled()) |
130 | |
{ |
131 | 0 | logger.debug("Files: " + files); |
132 | |
} |
133 | 35 | Comparator comparator = getComparator(); |
134 | 35 | if (comparator != null) |
135 | |
{ |
136 | 2 | Arrays.sort(files, comparator); |
137 | |
} |
138 | 85 | for (int i = 0; i < files.length; i++) |
139 | |
{ |
140 | |
|
141 | 53 | if (files[i].isFile()) |
142 | |
{ |
143 | 25 | this.processFile(files[i]); |
144 | |
} |
145 | |
} |
146 | |
} |
147 | 1 | catch (Exception e) |
148 | |
{ |
149 | 1 | this.handleException(e); |
150 | 32 | } |
151 | 33 | } |
152 | |
|
153 | |
public synchronized void processFile(final File sourceFile) throws MuleException |
154 | |
{ |
155 | |
|
156 | |
|
157 | |
|
158 | 25 | boolean checkFileAge = ((FileConnector) connector).getCheckFileAge(); |
159 | 25 | if (checkFileAge) |
160 | |
{ |
161 | 6 | long fileAge = ((FileConnector) connector).getFileAge(); |
162 | 6 | long lastMod = sourceFile.lastModified(); |
163 | 6 | long now = System.currentTimeMillis(); |
164 | 6 | long thisFileAge = now - lastMod; |
165 | 6 | if (thisFileAge < fileAge) |
166 | |
{ |
167 | 0 | if (logger.isDebugEnabled()) |
168 | |
{ |
169 | 0 | logger.debug("The file has not aged enough yet, will return nothing for: " + sourceFile); |
170 | |
} |
171 | 0 | return; |
172 | |
} |
173 | |
} |
174 | |
|
175 | |
|
176 | 25 | if (!attemptFileLock(sourceFile)) |
177 | |
{ |
178 | 0 | return; |
179 | |
} |
180 | |
|
181 | 25 | FileConnector fc = ((FileConnector) connector); |
182 | 25 | String sourceFileOriginalName = sourceFile.getName(); |
183 | |
|
184 | |
|
185 | 25 | if (!(sourceFile.canRead() && sourceFile.exists() && sourceFile.isFile())) |
186 | |
{ |
187 | 0 | throw new DefaultMuleException(FileMessages.fileDoesNotExist(sourceFileOriginalName)); |
188 | |
} |
189 | |
|
190 | |
|
191 | |
|
192 | |
|
193 | 25 | DefaultMessageAdapter fileParserMsgAdaptor = new DefaultMessageAdapter(null); |
194 | 25 | fileParserMsgAdaptor.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, sourceFileOriginalName); |
195 | |
|
196 | |
|
197 | 25 | File destinationFile = null; |
198 | 25 | if (moveDir != null) |
199 | |
{ |
200 | 8 | String destinationFileName = sourceFileOriginalName; |
201 | 8 | if (moveToPattern != null) |
202 | |
{ |
203 | 0 | destinationFileName = ((FileConnector) connector).getFilenameParser().getFilename(fileParserMsgAdaptor, |
204 | |
moveToPattern); |
205 | |
} |
206 | |
|
207 | 8 | destinationFile = FileUtils.newFile(moveDir, destinationFileName); |
208 | |
} |
209 | |
|
210 | 25 | MessageAdapter msgAdapter = null; |
211 | |
try |
212 | |
{ |
213 | 25 | if (fc.isStreaming()) |
214 | |
{ |
215 | 16 | msgAdapter = connector.getMessageAdapter(new ReceiverFileInputStream(sourceFile, fc.isAutoDelete(), |
216 | |
destinationFile)); |
217 | |
} |
218 | |
else |
219 | |
{ |
220 | 9 | msgAdapter = connector.getMessageAdapter(sourceFile); |
221 | |
} |
222 | |
} |
223 | 0 | catch (FileNotFoundException e) |
224 | |
{ |
225 | |
|
226 | 0 | logger.error("File being read disappeared!", e); |
227 | 0 | return; |
228 | 24 | } |
229 | 24 | msgAdapter.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, sourceFileOriginalName); |
230 | |
|
231 | 24 | if (!fc.isStreaming()) |
232 | |
{ |
233 | 8 | moveAndDelete(sourceFile, destinationFile, sourceFileOriginalName, msgAdapter); |
234 | |
} |
235 | |
else |
236 | |
{ |
237 | |
|
238 | |
|
239 | 16 | this.routeMessage(new DefaultMuleMessage(msgAdapter), endpoint.isSynchronous()); |
240 | |
} |
241 | 22 | } |
242 | |
|
243 | |
private void moveAndDelete(final File sourceFile, |
244 | |
File destinationFile, |
245 | |
String sourceFileOriginalName, |
246 | |
MessageAdapter msgAdapter) |
247 | |
{ |
248 | |
|
249 | 8 | boolean fileWasMoved = false; |
250 | |
|
251 | |
try |
252 | |
{ |
253 | |
|
254 | |
|
255 | |
|
256 | 8 | if (destinationFile != null) |
257 | |
{ |
258 | |
|
259 | 4 | fileWasMoved = FileUtils.moveFile(sourceFile, destinationFile); |
260 | |
|
261 | |
|
262 | 4 | if (!fileWasMoved) |
263 | |
{ |
264 | 0 | throw new DefaultMuleException(FileMessages.failedToMoveFile(sourceFile.getAbsolutePath(), |
265 | |
destinationFile.getAbsolutePath())); |
266 | |
} |
267 | |
|
268 | |
|
269 | 4 | msgAdapter = connector.getMessageAdapter(destinationFile); |
270 | |
|
271 | 4 | msgAdapter.setProperty(FileConnector.PROPERTY_FILENAME, destinationFile.getName()); |
272 | 4 | msgAdapter.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, sourceFileOriginalName); |
273 | |
} |
274 | |
|
275 | |
|
276 | 8 | this.routeMessage(new DefaultMuleMessage(msgAdapter), endpoint.isSynchronous()); |
277 | |
|
278 | |
|
279 | |
|
280 | 8 | if (((FileConnector) connector).isAutoDelete()) |
281 | |
{ |
282 | |
|
283 | 4 | if (destinationFile == null) |
284 | |
{ |
285 | |
|
286 | 2 | if (!sourceFile.delete()) |
287 | |
{ |
288 | 0 | throw new DefaultMuleException(FileMessages.failedToDeleteFile(sourceFile.getAbsolutePath())); |
289 | |
} |
290 | |
} |
291 | |
else |
292 | |
{ |
293 | |
|
294 | |
|
295 | |
} |
296 | |
} |
297 | |
} |
298 | 0 | catch (Exception e) |
299 | |
{ |
300 | 0 | boolean fileWasRolledBack = false; |
301 | |
|
302 | |
|
303 | 0 | if (fileWasMoved) |
304 | |
{ |
305 | 0 | fileWasRolledBack = rollbackFileMove(destinationFile, sourceFile.getAbsolutePath()); |
306 | |
} |
307 | |
|
308 | |
|
309 | 0 | Exception ex = new RoutingException(FileMessages.exceptionWhileProcessing(sourceFile.getName(), |
310 | |
(fileWasRolledBack ? "successful" : "unsuccessful")), new DefaultMuleMessage(msgAdapter), endpoint, e); |
311 | 0 | this.handleException(ex); |
312 | 8 | } |
313 | 8 | } |
314 | |
|
315 | |
|
316 | |
|
317 | |
|
318 | |
|
319 | |
|
320 | |
|
321 | |
|
322 | |
|
323 | |
protected boolean attemptFileLock(final File sourceFile) |
324 | |
{ |
325 | |
|
326 | |
|
327 | |
|
328 | |
|
329 | 25 | FileLock lock = null; |
330 | 25 | FileChannel channel = null; |
331 | 25 | boolean fileCanBeLocked = false; |
332 | |
try |
333 | |
{ |
334 | 25 | channel = new RandomAccessFile(sourceFile, "rw").getChannel(); |
335 | |
|
336 | |
|
337 | |
|
338 | 25 | lock = channel.tryLock(); |
339 | |
} |
340 | 0 | catch (FileNotFoundException fnfe) |
341 | |
{ |
342 | 0 | logger.warn("Unable to open " + sourceFile.getAbsolutePath(), fnfe); |
343 | |
} |
344 | 0 | catch (IOException e) |
345 | |
{ |
346 | |
|
347 | |
|
348 | |
|
349 | |
} |
350 | |
finally |
351 | |
{ |
352 | 25 | if (lock != null) |
353 | |
{ |
354 | |
|
355 | 25 | fileCanBeLocked = true; |
356 | |
try |
357 | |
{ |
358 | |
|
359 | 25 | lock.release(); |
360 | |
} |
361 | 0 | catch (IOException e) |
362 | |
{ |
363 | |
|
364 | 25 | } |
365 | |
} |
366 | |
|
367 | 25 | if (channel != null) |
368 | |
{ |
369 | |
try |
370 | |
{ |
371 | |
|
372 | 25 | channel.close(); |
373 | |
} |
374 | 0 | catch (IOException e) |
375 | |
{ |
376 | |
|
377 | 25 | } |
378 | |
} |
379 | |
} |
380 | |
|
381 | 25 | return fileCanBeLocked; |
382 | |
} |
383 | |
|
384 | |
|
385 | |
|
386 | |
|
387 | |
|
388 | |
|
389 | |
|
390 | |
|
391 | |
File[] listFiles() throws MuleException |
392 | |
{ |
393 | |
try |
394 | |
{ |
395 | 35 | File[] todoFiles = null; |
396 | 35 | if (fileFilter != null) |
397 | |
{ |
398 | 0 | todoFiles = readDirectory.listFiles(fileFilter); |
399 | |
} |
400 | |
else |
401 | |
{ |
402 | 35 | todoFiles = readDirectory.listFiles(filenameFilter); |
403 | |
} |
404 | |
|
405 | |
|
406 | 35 | return (todoFiles == null ? new File[0] : todoFiles); |
407 | |
} |
408 | 0 | catch (Exception e) |
409 | |
{ |
410 | 0 | throw new DefaultMuleException(FileMessages.errorWhileListingFiles(), e); |
411 | |
} |
412 | |
} |
413 | |
|
414 | |
|
415 | |
protected boolean rollbackFileMove(File sourceFile, String destinationFilePath) |
416 | |
{ |
417 | 0 | boolean result = false; |
418 | |
try |
419 | |
{ |
420 | 0 | result = FileUtils.moveFile(sourceFile, FileUtils.newFile(destinationFilePath)); |
421 | |
} |
422 | 0 | catch (Throwable t) |
423 | |
{ |
424 | 0 | logger.debug("rollback of file move failed: " + t.getMessage()); |
425 | 0 | } |
426 | 0 | return result; |
427 | |
} |
428 | |
|
429 | |
protected Comparator getComparator() throws Exception |
430 | |
{ |
431 | |
|
432 | 35 | Object o = getEndpoint().getProperty(COMPARATOR_CLASS_NAME_PROPERTY); |
433 | 35 | Object reverseProperty = this.getEndpoint().getProperty(COMPARATOR_REVERSE_ORDER_PROPERTY); |
434 | 35 | boolean reverse = false; |
435 | 35 | if (o != null) |
436 | |
{ |
437 | 2 | if (reverseProperty != null) |
438 | |
{ |
439 | 2 | reverse = Boolean.valueOf((String) reverseProperty).booleanValue(); |
440 | |
} |
441 | 2 | Class clazz = Class.forName(o.toString()); |
442 | 2 | o = clazz.newInstance(); |
443 | 2 | return reverse ? new ReverseComparator((Comparator) o) : (Comparator) o; |
444 | |
} |
445 | 33 | return null; |
446 | |
} |
447 | |
} |