View Javadoc

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