1
2
3
4
5
6
7
8
9
10
11 package org.mule.providers.ftp;
12
13 import org.mule.MuleRuntimeException;
14 import org.mule.config.i18n.CoreMessages;
15 import org.mule.config.i18n.MessageFactory;
16 import org.mule.impl.model.streaming.CallbackOutputStream;
17 import org.mule.providers.AbstractConnector;
18 import org.mule.providers.file.FilenameParser;
19 import org.mule.providers.file.SimpleFilenameParser;
20 import org.mule.umo.UMOComponent;
21 import org.mule.umo.UMOException;
22 import org.mule.umo.UMOMessage;
23 import org.mule.umo.endpoint.UMOEndpoint;
24 import org.mule.umo.endpoint.UMOEndpointURI;
25 import org.mule.umo.endpoint.UMOImmutableEndpoint;
26 import org.mule.umo.lifecycle.InitialisationException;
27 import org.mule.umo.provider.ConnectorException;
28 import org.mule.umo.provider.DispatchException;
29 import org.mule.umo.provider.UMOMessageReceiver;
30 import org.mule.util.ClassUtils;
31
32 import java.io.IOException;
33 import java.io.OutputStream;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.Map;
37
38 import org.apache.commons.net.ftp.FTP;
39 import org.apache.commons.net.ftp.FTPClient;
40 import org.apache.commons.pool.ObjectPool;
41 import org.apache.commons.pool.impl.GenericObjectPool;
42
43 public class FtpConnector extends AbstractConnector
44 {
45 public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
46 public static final String PROPERTY_FILENAME = "filename";
47 public static final String PROPERTY_OUTPUT_PATTERN = "outputPattern";
48 public static final String PROPERTY_PASSIVE_MODE = "passive";
49 public static final String PROPERTY_BINARY_TRANSFER = "binary";
50
51 public static final int DEFAULT_POLLING_FREQUENCY = 1000;
52
53
54
55
56
57 public static final String DEFAULT_FTP_CONNECTION_FACTORY_CLASS = "org.mule.providers.ftp.FtpConnectionFactory";
58
59
60
61
62 private long pollingFrequency;
63
64 private String outputPattern;
65
66 private FilenameParser filenameParser = new SimpleFilenameParser();
67
68 private boolean passive = true;
69
70 private boolean binary = true;
71
72
73
74
75 private boolean validateConnections = true;
76
77 private Map pools = new HashMap();
78
79 private String connectionFactoryClass = DEFAULT_FTP_CONNECTION_FACTORY_CLASS;
80
81 public String getProtocol()
82 {
83 return "ftp";
84 }
85
86 public UMOMessageReceiver createReceiver(UMOComponent component, UMOEndpoint endpoint) throws Exception
87 {
88 long polling = pollingFrequency;
89 Map props = endpoint.getProperties();
90 if (props != null)
91 {
92
93 String tempPolling = (String) props.get(PROPERTY_POLLING_FREQUENCY);
94 if (tempPolling != null)
95 {
96 polling = Long.parseLong(tempPolling);
97 }
98 }
99 if (polling <= 0)
100 {
101 polling = DEFAULT_POLLING_FREQUENCY;
102 }
103 logger.debug("set polling frequency to " + polling);
104 return serviceDescriptor.createMessageReceiver(this, component, endpoint,
105 new Object[]{new Long(polling)});
106 }
107
108
109
110
111 public long getPollingFrequency()
112 {
113 return pollingFrequency;
114 }
115
116
117
118
119 public void setPollingFrequency(long pollingFrequency)
120 {
121 this.pollingFrequency = pollingFrequency;
122 }
123
124
125
126
127
128
129 public String getConnectionFactoryClass()
130 {
131 return connectionFactoryClass;
132 }
133
134
135
136
137
138
139
140 public void setConnectionFactoryClass(final String connectionFactoryClass)
141 {
142 this.connectionFactoryClass = connectionFactoryClass;
143 }
144
145 public FTPClient getFtp(UMOEndpointURI uri) throws Exception
146 {
147 if (logger.isDebugEnabled())
148 {
149 logger.debug(">>> retrieving client for " + uri);
150 }
151 return (FTPClient) getFtpPool(uri).borrowObject();
152 }
153
154 public void releaseFtp(UMOEndpointURI uri, FTPClient client) throws Exception
155 {
156 if (logger.isDebugEnabled())
157 {
158 logger.debug("<<< releasing client for " + uri);
159 }
160 if (dispatcherFactory.isCreateDispatcherPerRequest())
161 {
162 destroyFtp(uri, client);
163 }
164 else
165 {
166 getFtpPool(uri).returnObject(client);
167 }
168 }
169
170 public void destroyFtp(UMOEndpointURI uri, FTPClient client) throws Exception
171 {
172 if (logger.isDebugEnabled())
173 {
174 logger.debug("<<< destroying client for " + uri);
175 }
176 try
177 {
178 getFtpPool(uri).invalidateObject(client);
179 }
180 catch (Exception e)
181 {
182
183 logger.debug(e.getMessage());
184 }
185 }
186
187 protected synchronized ObjectPool getFtpPool(UMOEndpointURI uri)
188 {
189 if (logger.isDebugEnabled())
190 {
191 logger.debug("=== get pool for " + uri);
192 }
193 String key = uri.getUsername() + ":" + uri.getPassword() + "@" + uri.getHost() + ":" + uri.getPort();
194 ObjectPool pool = (ObjectPool) pools.get(key);
195 if (pool == null)
196 {
197 try
198 {
199 FtpConnectionFactory connectionFactory =
200 (FtpConnectionFactory) ClassUtils.instanciateClass(getConnectionFactoryClass(),
201 new Object[] {uri}, getClass());
202 pool = new GenericObjectPool(connectionFactory);
203 ((GenericObjectPool) pool).setTestOnBorrow(this.validateConnections);
204 pools.put(key, pool);
205 }
206 catch (Exception ex)
207 {
208 throw new MuleRuntimeException(
209 MessageFactory.createStaticMessage("Hmm, couldn't instanciate FTP connection factory."), ex);
210 }
211 }
212 return pool;
213 }
214
215
216 protected void doInitialise() throws InitialisationException
217 {
218 try
219 {
220 Class objectFactoryClass = ClassUtils.loadClass(this.connectionFactoryClass, getClass());
221 if (!FtpConnectionFactory.class.isAssignableFrom(objectFactoryClass))
222 {
223 throw new InitialisationException(MessageFactory.createStaticMessage(
224 "FTP connectionFactoryClass is not an instance of org.mule.providers.ftp.FtpConnectionFactory"),
225 this);
226 }
227 }
228 catch (ClassNotFoundException e)
229 {
230 throw new InitialisationException(e, this);
231 }
232 }
233
234 protected void doDispose()
235 {
236
237 }
238
239 protected void doConnect() throws Exception
240 {
241
242 }
243
244 protected void doDisconnect() throws Exception
245 {
246
247 }
248
249 protected void doStart() throws UMOException
250 {
251
252 }
253
254 protected void doStop() throws UMOException
255 {
256 if (logger.isDebugEnabled())
257 {
258 logger.debug("!!! stopping all pools");
259 }
260 try
261 {
262 for (Iterator it = pools.values().iterator(); it.hasNext();)
263 {
264 ObjectPool pool = (ObjectPool)it.next();
265 pool.close();
266 }
267 }
268 catch (Exception e)
269 {
270 throw new ConnectorException(CoreMessages.failedToStop("FTP Connector"), this, e);
271 }
272 }
273
274
275
276
277 public String getOutputPattern()
278 {
279 return outputPattern;
280 }
281
282
283
284
285 public void setOutputPattern(String outputPattern)
286 {
287 this.outputPattern = outputPattern;
288 }
289
290
291
292
293 public FilenameParser getFilenameParser()
294 {
295 return filenameParser;
296 }
297
298
299
300
301 public void setFilenameParser(FilenameParser filenameParser)
302 {
303 this.filenameParser = filenameParser;
304 }
305
306
307
308
309
310
311 public boolean isPassive()
312 {
313 return passive;
314 }
315
316
317
318
319
320
321 public void setPassive(final boolean passive)
322 {
323 this.passive = passive;
324 }
325
326
327
328
329
330
331
332
333 public void enterActiveOrPassiveMode(FTPClient client, UMOImmutableEndpoint endpoint)
334 {
335
336
337 final String passiveString = (String)endpoint.getProperty(FtpConnector.PROPERTY_PASSIVE_MODE);
338 if (passiveString == null)
339 {
340
341 if (isPassive())
342 {
343 if (logger.isTraceEnabled())
344 {
345 logger.trace("Entering FTP passive mode");
346 }
347 client.enterLocalPassiveMode();
348 }
349 else
350 {
351 if (logger.isTraceEnabled())
352 {
353 logger.trace("Entering FTP active mode");
354 }
355 client.enterLocalActiveMode();
356 }
357 }
358 else
359 {
360
361 final boolean passiveMode = Boolean.valueOf(passiveString).booleanValue();
362 if (passiveMode)
363 {
364 if (logger.isTraceEnabled())
365 {
366 logger.trace("Entering FTP passive mode (endpoint override)");
367 }
368 client.enterLocalPassiveMode();
369 }
370 else
371 {
372 if (logger.isTraceEnabled())
373 {
374 logger.trace("Entering FTP active mode (endpoint override)");
375 }
376 client.enterLocalActiveMode();
377 }
378 }
379 }
380
381
382
383
384 public boolean isValidateConnections()
385 {
386 return validateConnections;
387 }
388
389
390
391
392
393
394
395
396 public void setValidateConnections(final boolean validateConnections)
397 {
398 this.validateConnections = validateConnections;
399 }
400
401
402
403
404
405
406 public boolean isBinary()
407 {
408 return binary;
409 }
410
411
412
413
414
415
416 public void setBinary(final boolean binary)
417 {
418 this.binary = binary;
419 }
420
421
422
423
424
425
426
427
428 public void setupFileType(FTPClient client, UMOImmutableEndpoint endpoint) throws Exception
429 {
430 int type;
431
432
433
434 final String binaryTransferString = (String)endpoint.getProperty(FtpConnector.PROPERTY_BINARY_TRANSFER);
435 if (binaryTransferString == null)
436 {
437
438 if (isBinary())
439 {
440 if (logger.isTraceEnabled())
441 {
442 logger.trace("Using FTP BINARY type");
443 }
444 type = FTP.BINARY_FILE_TYPE;
445 }
446 else
447 {
448 if (logger.isTraceEnabled())
449 {
450 logger.trace("Using FTP ASCII type");
451 }
452 type = FTP.ASCII_FILE_TYPE;
453 }
454 }
455 else
456 {
457
458 final boolean binaryTransfer = Boolean.valueOf(binaryTransferString).booleanValue();
459 if (binaryTransfer)
460 {
461 if (logger.isTraceEnabled())
462 {
463 logger.trace("Using FTP BINARY type (endpoint override)");
464 }
465 type = FTP.BINARY_FILE_TYPE;
466 }
467 else
468 {
469 if (logger.isTraceEnabled())
470 {
471 logger.trace("Using FTP ASCII type (endpoint override)");
472 }
473 type = FTP.ASCII_FILE_TYPE;
474 }
475 }
476
477 client.setFileType(type);
478 }
479
480
481
482
483
484
485
486
487
488
489
490 public OutputStream getOutputStream(UMOImmutableEndpoint endpoint, UMOMessage message)
491 throws UMOException
492 {
493 try
494 {
495 final UMOEndpointURI uri = endpoint.getEndpointURI();
496 String filename = getFilename(endpoint, message);
497 final FTPClient client = getFtp(uri);
498
499 try
500 {
501 enterActiveOrPassiveMode(client, endpoint);
502 setupFileType(client, endpoint);
503 if (!client.changeWorkingDirectory(uri.getPath()))
504 {
505 throw new IOException("Ftp error: " + client.getReplyCode());
506 }
507 OutputStream out = client.storeFileStream(filename);
508 return new CallbackOutputStream(out,
509 new CallbackOutputStream.Callback()
510 {
511 public void onClose() throws Exception
512 {
513 try
514 {
515 if (!client.completePendingCommand())
516 {
517 client.logout();
518 client.disconnect();
519 throw new IOException("FTP Stream failed to complete pending request");
520 }
521 }
522 finally
523 {
524 releaseFtp(uri, client);
525 }
526 }
527 });
528 }
529 catch (Exception e)
530 {
531 logger.debug("Error getting output stream: ", e);
532 releaseFtp(uri, client);
533 throw e;
534 }
535 }
536 catch (Exception e)
537 {
538 throw new DispatchException(CoreMessages.streamingFailedNoStream(), message, endpoint, e);
539 }
540 }
541
542 private String getFilename(UMOImmutableEndpoint endpoint, UMOMessage message) throws IOException
543 {
544 String filename = (String)message.getProperty(FtpConnector.PROPERTY_FILENAME);
545 if (filename == null)
546 {
547 String outPattern = (String) endpoint.getProperty(FtpConnector.PROPERTY_OUTPUT_PATTERN);
548 if (outPattern == null){
549 outPattern = message.getStringProperty(FtpConnector.PROPERTY_OUTPUT_PATTERN,
550 getOutputPattern());
551 }
552 filename = generateFilename(message, outPattern);
553 }
554 if (filename == null)
555 {
556 throw new IOException("Filename is null");
557 }
558 return filename;
559 }
560
561 private String generateFilename(UMOMessage message, String pattern)
562 {
563 if (pattern == null)
564 {
565 pattern = getOutputPattern();
566 }
567 return getFilenameParser().getFilename(message, pattern);
568 }
569
570 }