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