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