View Javadoc

1   /*
2    * $Id: FileConnector.java 10483 2008-01-23 11:57:53Z holger $
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.file;
12  
13  import org.mule.config.MuleProperties;
14  import org.mule.config.i18n.CoreMessages;
15  import org.mule.providers.AbstractConnector;
16  import org.mule.providers.file.filters.FilenameWildcardFilter;
17  import org.mule.transformers.NoActionTransformer;
18  import org.mule.transformers.simple.ByteArrayToSerializable;
19  import org.mule.transformers.simple.SerializableToByteArray;
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.UMOImmutableEndpoint;
25  import org.mule.umo.lifecycle.InitialisationException;
26  import org.mule.umo.provider.DispatchException;
27  import org.mule.umo.provider.UMOMessageReceiver;
28  import org.mule.util.FileUtils;
29  import org.mule.util.MapUtils;
30  
31  import java.io.File;
32  import java.io.FileOutputStream;
33  import java.io.IOException;
34  import java.io.OutputStream;
35  import java.util.Map;
36  import java.util.Properties;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /**
42   * <code>FileConnector</code> is used for setting up listeners on a directory and
43   * for writing files to a directory. The connecotry provides support for defining
44   * file output patterns and filters for receiving files.
45   */
46  
47  public class FileConnector extends AbstractConnector
48  {
49      /**
50       * logger used by this class
51       */
52      private static Log logger = LogFactory.getLog(FileConnector.class);
53  
54      // These are properties that can be overridden on the Receiver by the endpoint
55      // declaration
56      public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
57      public static final String PROPERTY_FILE_AGE = "fileAge";
58      public static final String PROPERTY_FILENAME = "filename";
59      public static final String PROPERTY_ORIGINAL_FILENAME = "originalFilename";
60      public static final String PROPERTY_OUTPUT_PATTERN = "outputPattern";
61      public static final String PROPERTY_MOVE_TO_PATTERN = "moveToPattern";
62      public static final String PROPERTY_MOVE_TO_DIRECTORY = "moveToDirectory";
63      public static final String PROPERTY_DELETE_ON_READ = "autoDelete";
64      public static final String PROPERTY_DIRECTORY = "directory";
65      public static final String PROPERTY_SERVICE_OVERRIDE = "serviceOverrides";
66      public static final String PROPERTY_WRITE_TO_DIRECTORY = "writeToDirectoryName";
67      public static final String PROPERTY_FILE_SIZE = "fileSize";
68  
69      public static final long DEFAULT_POLLING_FREQUENCY = 1000;
70  
71  
72      /**
73       * Time in milliseconds to poll. On each poll the poll() method is called
74       */
75      private long pollingFrequency = 0;
76  
77      private String moveToPattern = null;
78  
79      private String writeToDirectoryName = null;
80  
81      private String moveToDirectoryName = null;
82  
83      private String outputPattern = null;
84  
85      private boolean outputAppend = false;
86  
87      private boolean autoDelete = true;
88  
89      private boolean checkFileAge = false;
90  
91      private long fileAge = 0;
92  
93      private FileOutputStream outputStream = null;
94  
95      private boolean serialiseObjects = false;
96  
97      public FilenameParser filenameParser;
98  
99      /*
100      * (non-Javadoc)
101      * 
102      * @see org.mule.providers.AbstractConnector#doInitialise()
103      */
104     public FileConnector()
105     {
106         super();
107         // MULE-1773: limit the number of dispatchers per endpoint to 1 until
108         // there is a proper (Distributed)LockManager in place (MULE-2402).
109         // We also override the setter to prevent "wrong" configuration for now.
110         super.setMaxDispatchersActive(1);
111         filenameParser = new SimpleFilenameParser();
112     }
113 
114     // @Override
115     public void setMaxDispatchersActive(int value)
116     {
117         if (value != 1)
118         {
119             throw new IllegalArgumentException("MULE-1773: cannot configure maxDispatchersActive");
120         }
121     }
122 
123     // @Override
124     protected Object getReceiverKey(UMOComponent component, UMOEndpoint endpoint)
125     {
126         if (endpoint.getFilter() != null)
127         {
128             return endpoint.getEndpointURI().getAddress() + "/"
129                    + ((FilenameWildcardFilter) endpoint.getFilter()).getPattern();
130         }
131         return endpoint.getEndpointURI().getAddress();
132     }
133 
134     /**
135      * Registers a listener for a particular directory The following properties can
136      * be overriden in the endpoint declaration
137      * <ul>
138      * <li>moveToDirectory</li>
139      * <li>filterPatterns</li>
140      * <li>filterClass</li>
141      * <li>pollingFrequency</li>
142      * </ul>
143      */
144     public UMOMessageReceiver createReceiver(UMOComponent component, UMOEndpoint endpoint) throws Exception
145     {
146         String readDir = endpoint.getEndpointURI().getAddress();
147         long polling = this.pollingFrequency;
148 
149         String moveTo = moveToDirectoryName;
150         String moveToPattern = getMoveToPattern();
151 
152         Map props = endpoint.getProperties();
153         if (props != null)
154         {
155             // Override properties on the endpoint for the specific endpoint
156             String move = (String) props.get(PROPERTY_MOVE_TO_DIRECTORY);
157             if (move != null)
158             {
159                 moveTo = move;
160             }
161             String tempMoveToPattern = (String) props.get(PROPERTY_MOVE_TO_PATTERN);
162             if (tempMoveToPattern != null)
163             {
164                 if (logger.isDebugEnabled())
165                 {
166                     logger.debug("set moveTo Pattern to: " + tempMoveToPattern);
167                 }
168                 moveToPattern = tempMoveToPattern;
169             }
170 
171             String tempPolling = (String) props.get(PROPERTY_POLLING_FREQUENCY);
172             if (tempPolling != null)
173             {
174                 polling = Long.parseLong(tempPolling);
175             }
176 
177             if (polling <= 0)
178             {
179                 polling = DEFAULT_POLLING_FREQUENCY;
180             }
181 
182             if (logger.isDebugEnabled())
183             {
184                 logger.debug("set polling frequency to: " + polling);
185             }
186             String tempFileAge = (String) props.get(PROPERTY_FILE_AGE);
187             if (tempFileAge != null)
188             {
189                 try
190                 {
191                     setFileAge(Long.parseLong(tempFileAge));
192                 }
193                 catch (Exception ex1)
194                 {
195                     logger.error("Failed to set fileAge", ex1);
196                 }
197             }
198             Map srvOverride = (Map) props.get(PROPERTY_SERVICE_OVERRIDE);
199             if (srvOverride != null) 
200             {
201                 if (serviceOverrides == null) 
202                 {
203                     serviceOverrides = new Properties();
204                 }
205                 serviceOverrides.setProperty(MuleProperties.CONNECTOR_INBOUND_TRANSFORMER,
206                     NoActionTransformer.class.getName());
207                 serviceOverrides.setProperty(MuleProperties.CONNECTOR_OUTBOUND_TRANSFORMER,
208                     NoActionTransformer.class.getName());
209             }
210         }
211 
212         try
213         {
214             return serviceDescriptor.createMessageReceiver(this, component, endpoint, new Object[]{readDir,
215                 moveTo, moveToPattern, new Long(polling)});
216 
217         }
218         catch (Exception e)
219         {
220             throw new InitialisationException(
221                 CoreMessages.failedToCreateObjectWith("Message Receiver", 
222                     serviceDescriptor.getMessageReceiver()), e, this);
223         }
224     }
225 
226     public String getProtocol()
227     {
228         return "file";
229     }
230 
231     public FilenameParser getFilenameParser()
232     {
233         return filenameParser;
234     }
235 
236     public void setFilenameParser(FilenameParser filenameParser)
237     {
238         this.filenameParser = filenameParser;
239     }
240 
241     protected void doDispose()
242     {
243         try
244         {
245             doStop();
246         }
247         catch (UMOException e)
248         {
249             logger.error(e.getMessage(), e);
250         }
251     }
252 
253 
254     protected void doInitialise() throws InitialisationException
255     {
256         // template method, nothing to do
257     }
258 
259     protected void doConnect() throws Exception
260     {
261         // template method, nothing to do
262     }
263 
264     protected void doDisconnect() throws Exception
265     {
266         // template method, nothing to do
267     }
268 
269     protected void doStart() throws UMOException
270     {
271         // template method, nothing to do
272     }
273 
274     protected void doStop() throws UMOException
275     {
276         if (outputStream != null)
277         {
278             try
279             {
280                 outputStream.close();
281             }
282             catch (IOException e)
283             {
284                 logger.warn("Failed to close file output stream on stop: " + e);
285             }
286         }
287     }
288 
289     /**
290      * @return Returns the moveToDirectoryName.
291      */
292     public String getMoveToDirectory()
293     {
294         return moveToDirectoryName;
295     }
296 
297     /**
298      * @param dir The moveToDirectoryName to set.
299      */
300     public void setMoveToDirectory(String dir)
301     {
302         this.moveToDirectoryName = dir;
303     }
304 
305     /**
306      * @return Returns the outputAppend.
307      */
308     public boolean isOutputAppend()
309     {
310         return outputAppend;
311     }
312 
313     /**
314      * @param outputAppend The outputAppend to set.
315      */
316     public void setOutputAppend(boolean outputAppend)
317     {
318         this.outputAppend = outputAppend;
319     }
320 
321     /**
322      * @return Returns the outputPattern.
323      */
324     public String getOutputPattern()
325     {
326         return outputPattern;
327     }
328 
329     /**
330      * @param outputPattern The outputPattern to set.
331      */
332     public void setOutputPattern(String outputPattern)
333     {
334         this.outputPattern = outputPattern;
335     }
336 
337     /**
338      * @return Returns the outputStream.
339      */
340     public FileOutputStream getOutputStream()
341     {
342         return outputStream;
343     }
344 
345     /**
346      * @param outputStream The outputStream to set.
347      */
348     public void setOutputStream(FileOutputStream outputStream)
349     {
350         this.outputStream = outputStream;
351     }
352 
353     /**
354      * @return Returns the pollingFrequency.
355      */
356     public long getPollingFrequency()
357     {
358         return pollingFrequency;
359     }
360 
361     /**
362      * @param pollingFrequency The pollingFrequency to set.
363      */
364     public void setPollingFrequency(long pollingFrequency)
365     {
366         this.pollingFrequency = pollingFrequency;
367     }
368 
369     /**
370      * @return Returns the fileAge.
371      */
372     public long getFileAge()
373     {
374         return fileAge;
375     }
376 
377     public boolean getCheckFileAge()
378     {
379         return checkFileAge;
380     }
381 
382     /**
383      * @param fileAge The fileAge in milliseconds to set.
384      */
385     public void setFileAge(long fileAge)
386     {
387         this.fileAge = fileAge;
388         this.checkFileAge = true;
389     }
390 
391     /**
392      * @return Returns the writeToDirectory.
393      */
394     public String getWriteToDirectory()
395     {
396         return writeToDirectoryName;
397     }
398 
399     /**
400      * @param dir The writeToDirectory to set.
401      */
402     public void setWriteToDirectory(String dir) throws IOException
403     {
404         this.writeToDirectoryName = dir;
405         if (writeToDirectoryName != null)
406         {
407             File writeToDirectory = FileUtils.openDirectory((writeToDirectoryName));
408             if (!(writeToDirectory.canRead()) || !writeToDirectory.canWrite())
409             {
410                 throw new IOException(
411                     "Error on initialization, Write To directory does not exist or is not read/write");
412             }
413         }
414     }
415 
416     public boolean isSerialiseObjects()
417     {
418         return serialiseObjects;
419     }
420 
421     public void setSerialiseObjects(boolean serialiseObjects)
422     {
423         // set serialisable transformers on the connector if this is set
424         if (serialiseObjects)
425         {
426             if (serviceOverrides == null)
427             {
428                 serviceOverrides = new Properties();
429             }
430             serviceOverrides.setProperty(MuleProperties.CONNECTOR_INBOUND_TRANSFORMER,
431                 ByteArrayToSerializable.class.getName());
432             serviceOverrides.setProperty(MuleProperties.CONNECTOR_OUTBOUND_TRANSFORMER,
433                 SerializableToByteArray.class.getName());
434         }
435 
436         this.serialiseObjects = serialiseObjects;
437     }
438 
439     public boolean isAutoDelete()
440     {
441         return autoDelete;
442     }
443 
444     public void setAutoDelete(boolean autoDelete)
445     {
446         this.autoDelete = autoDelete;
447         if (!autoDelete)
448         {
449             if (serviceOverrides == null)
450             {
451                 serviceOverrides = new Properties();
452             }
453             if (serviceOverrides.getProperty(MuleProperties.CONNECTOR_MESSAGE_ADAPTER) == null)
454             {
455                 serviceOverrides.setProperty(MuleProperties.CONNECTOR_MESSAGE_ADAPTER,
456                     FileMessageAdapter.class.getName());
457             }
458         }
459     }
460 
461     public String getMoveToPattern()
462     {
463         return moveToPattern;
464     }
465 
466     public void setMoveToPattern(String moveToPattern)
467     {
468         this.moveToPattern = moveToPattern;
469     }
470 
471     /**
472      * Well get the output stream (if any) for this type of transport. Typically this
473      * will be called only when Streaming is being used on an outbound endpoint
474      *
475      * @param endpoint the endpoint that releates to this Dispatcher
476      * @param message the current message being processed
477      * @return the output stream to use for this request or null if the transport
478      *         does not support streaming
479      * @throws org.mule.umo.UMOException
480      */
481     public OutputStream getOutputStream(UMOImmutableEndpoint endpoint, UMOMessage message)
482         throws UMOException
483     {
484         String address = endpoint.getEndpointURI().getAddress();
485         String writeToDirectory = message.getStringProperty(
486             FileConnector.PROPERTY_WRITE_TO_DIRECTORY, null);
487         if (writeToDirectory == null)
488         {
489             writeToDirectory = getWriteToDirectory();
490         }
491         if (writeToDirectory != null)
492         {
493             address = getFilenameParser().getFilename(message, writeToDirectory);
494         }
495 
496         String filename;
497         String outPattern = (String)endpoint.getProperty(FileConnector.PROPERTY_OUTPUT_PATTERN);
498         if (outPattern == null)
499         {
500             outPattern = message.getStringProperty(FileConnector.PROPERTY_OUTPUT_PATTERN, null);
501         }
502         if (outPattern == null)
503         {
504             outPattern = getOutputPattern();
505         }
506         try
507         {
508             if (outPattern != null)
509             {
510                 filename = generateFilename(message, outPattern);
511             }
512             else
513             {
514                 filename = message.getStringProperty(FileConnector.PROPERTY_FILENAME, null);
515                 if (filename == null)
516                 {
517                     filename = generateFilename(message, null);
518                 }
519             }
520 
521             if (filename == null)
522             {
523                 throw new IOException("Filename is null");
524             }
525             File file = FileUtils.createFile(address + "/" + filename);
526             if (logger.isInfoEnabled())
527             {
528                 logger.info("Writing file to: " + file.getAbsolutePath());
529             }
530 
531             return new FileOutputStream(file, MapUtils.getBooleanValue(endpoint.getProperties(),
532                 "outputAppend", isOutputAppend()));
533         }
534         catch (IOException e)
535         {
536             throw new DispatchException(CoreMessages.streamingFailedNoStream(), message,
537                 endpoint, e);
538         }
539     }
540 
541     private String generateFilename(UMOMessage message, String pattern)
542     {
543         if (pattern == null)
544         {
545             pattern = getOutputPattern();
546         }
547         return getFilenameParser().getFilename(message, pattern);
548     }
549 }