Tuesday, April 10, 2018

Define and consume custom Java methods with Dataweave 2.0 in Mule 4


With the introduction of a brand-new Java module in Mule 4, it is now possible to invoke Java classes in a more straightforward manner from the flow or in Dataweave 2.0 itself.

Here is a simple demonstration of file manipulation using custom Java classes and methods in Mule Anypoint Studio 7.1. This step-by-step tutorial shows how to:
1. Create a custom java class to list files recursively for a local or shared network path
2. Invoking this class from Mulesoft Dataweave 2.0 transformations, and filter information.

Pre-requisites:
There is a dependency on apache commons-io, log4j and joda-time as I will be reusing some methods for file manipulation.
You can find the jar file or maven references here:
https://mvnrepository.com/artifact/commons-io/commons-io/2.6
https://mvnrepository.com/artifact/joda-time/joda-time
https://mvnrepository.com/artifact/log4j/log4j


1. Create your Mule project and add maven dependency
Download the apache commons-io jar file, or alternatively include the reference in your maven pom.xml file.
For this example, I am going to include it as a maven dependency. Copy and paste the code below and include it between the dependencies node in pom.xml, before the closing node:


 <dependency>  
   <groupId>commons-io</groupId>  
   <artifactId>commons-io</artifactId>  
   <version>2.6</version>  
 </dependency>  

Side note: In my custom class I am also making use of log4j and joda so those are included as maven dependencies as well.
 <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->  
 <dependency>  
   <groupId>joda-time</groupId>  
   <artifactId>joda-time</artifactId>  
   <version>2.3</version>  
 </dependency>  
 <!-- https://mvnrepository.com/artifact/log4j/log4j -->  
 <dependency>  
   <groupId>log4j</groupId>  
   <artifactId>log4j</artifactId>  
   <version>1.2.17</version>  
 </dependency>  

2. Create custom Java class and methods
Next, create a custom Java class in your Mule project by right clicking on the src/main/java/ folder -> New -> Class



I am going to name my class CustomFileUtils with the namespace customUtils.
Inside my class I am going to declare 3 static methods: listFilesByExtension, listOldFiles, deleteOldFiles

(Now do take note that Mule4 does have a comprehensive File List component that does the same thing (list files in a directory). It returns a collection of File objects. However, I prefer something more lightweight, e.g. returning just the file paths for further manipulation. Besides, I wanted to be able to traverse sub folders as well.)

My custom Java class:
 package customUtils;  
 import java.io.File;  
 import java.util.ArrayList;  
 import java.util.Collection;  
 import org.apache.commons.io.FileUtils;  
 import org.apache.commons.io.filefilter.AgeFileFilter;  
 import org.apache.commons.io.filefilter.TrueFileFilter;  
 import org.joda.time.DateTime;  
 import org.apache.log4j.Logger;  
 public class CustomFileUtil {  
      private static Logger log = Logger.getLogger(CustomFileUtil.class);  
      // List files by passing in extensions to filter on  
      public static ArrayList<String> listFileNames(String path, String[] extensions, boolean recursive) {  
        File src = new java.io.File(path);  
           if (!src.exists()) return null;  
        Collection<File> files = FileUtils.listFiles(src, extensions, recursive);  
        ArrayList<String> classes = new ArrayList<String>();  
        for (File file : files) {  
          classes.add(file.getPath());  
        }  
        return classes;  
      }  
      // A way to clear files in any directory by passing in the days  
      public static ArrayList<String> deleteOldFiles(String dir, int daysToRemainFiles) {  
        File src = new java.io.File(dir);  
           if (!src.exists()) return null;  
        ArrayList<String> deletedFiles = new ArrayList<String>();  
        int failedDeletion = 0;  
        // List files older than x days  
        Collection<File> filesToDelete = FileUtils.listFiles(src,  
            new AgeFileFilter(DateTime.now().withTimeAtStartOfDay().minusDays(daysToRemainFiles).toDate()),  
            TrueFileFilter.TRUE);  // include sub dirs  
        for (File file : filesToDelete) {  
             deletedFiles.add(file.getPath());  
          boolean success = FileUtils.deleteQuietly(file);  
          if (!success) {  
            // log to Mule's log4j  
               log.info("Failed to delete file " + file.getPath());  
               failedDeletion += 1;  
          }  
        }  
        // Log total files processed and deleted  
        log.info("Total files found: " + filesToDelete.size() + ", failed to delete " + failedDeletion);  
        return deletedFiles;  
      }  
      // List files in any directory by passing in the days  
      public static ArrayList<String> listOldFiles(String dir, int daysToRemainFiles) {  
        File src = new java.io.File(dir);  
           if (!src.exists()) return null;  
        ArrayList<String> oldFiles = new ArrayList<String>();  
        // List files older than x days  
        Collection<File> filesToDelete = FileUtils.listFiles(src,  
            new AgeFileFilter(DateTime.now().withTimeAtStartOfDay().minusDays(daysToRemainFiles).toDate()),  
            TrueFileFilter.TRUE);  // include sub dirs  
        for (File file : filesToDelete) {  
             oldFiles.add(file.getPath());  
        }  
        return oldFiles;  
      }  
 }  

3. Add the namespace for the custom class
In order to include this as a reference in my project, I need to add the namespace to mule-artifact.json as below:


4. Add your flow with Dataweave transformation
Below is a simple flow with a HTTP listener which will accept a POST value named path containing the full path to a folder or network directory.



To reference a custom Java class, you need to add the import java!:: statement. In the example below, I reference a public static method exposed by the custom Class. To recap, the method was:
public static ArrayList listFileNames(String path, String[] extensions, boolean recursive) {}


** Take note that at the time of writing, Dataweave 2.0 does not support having period (.) in the package name.
For example, if my class name was com.something.CustomFileUtil instead of CustomFileUtil, the Dataweave parsing will throw an error.

You can pass in an array of extensions if you would like to list certain types of files:


The complete flow xml:


 <flow name="listFilesWithDataweaveFlow" doc:id="22bac947-3406-484e-bb61-2cd418aac08d" >  
      <http:listener doc:name="Listener" doc:id="247a6d13-5750-43e4-80ac-96ff23064799" config-ref="HTTP_Listener_config" path="list-files" allowedMethods="POST"/>  
      <ee:transform doc:name="Transform Message" doc:id="4ba2a858-0b3c-4d2d-9c5c-71b509147bdb" >  
           <ee:message >  
                <ee:set-payload ><![CDATA[%dw 2.0  
 import java!customUtils::CustomFileUtil  
 output application/json  
 ---  
 {  
 files: CustomFileUtil::listFileNames(payload.path as String, ["txt", "pdf"], false)  
 }]]></ee:set-payload>  
           </ee:message>  
      </ee:transform>  
 </flow>  

5. Test your flow
Test it in a tool such as POSTMAN and you should be able to retrieve a list of file paths matching the txt or pdf extension like below.

The request headers:


Response:

3 comments:

Unknown said...

Thank you for sharing wonderful information with us.
MuleSoft Online Training
MuleSoft Training in Hyderabad

goformule said...

Very informative post for mulesoft developers.You can also visit goformule.com for mulesoft stuff.

Sowmiya R said...

This is good information and really helpful for the people who need information about this.
oracle training in chennai

oracle training institute in chennai

oracle training in bangalore

oracle training in hyderabad

oracle training

hadoop training in chennai

hadoop training in bangalore

Related Posts Plugin for WordPress, Blogger...