Create zip file in memory and on Disk

In context of Mule3, if we need to create zip file with n number of files in it, where n will keep changing as per our requirement.

2 ways in which we can achieve this

  1. Create a unique folder per request, and dump all the required files inside this folder, which will finally be zipped.
  2. Create ByteArrayOutputStream, attach it to ZipOutputStream and then create a ZipEntry associate every ZipEntry (Write) to ZipOutputStream, finally ByteArrayOutputStream would be our stream of zipfile content.

 

Approach 1 :

  • Numerous ways of doing this one of the way is using walk file tree feature of Java NIO, where in we can zip the entire directory and sub-directory, exactly what we need.
  • For Indepth understanding one of the good write ups, https://www.baeldung.com/java-nio2-file-visitor

Sample Code :

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.file.*;

import java.nio.file.attribute.BasicFileAttributes;

import java.util.zip.ZipEntry;

import java.util.zip.ZipOutputStream;

 

public class ZipCompress {

public static void compress(String dirPath) {

final Path sourceDir = Paths.get(dirPath);

String zipFileName = dirPath.concat(“.zip”);

try {

final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFileName));

Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {

@Override

public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {

try {

Path targetFile = sourceDir.relativize(file);

outputStream.putNextEntry(new ZipEntry(targetFile.toString()));

byte[] bytes = Files.readAllBytes(file);

outputStream.write(bytes, 0, bytes.length);

outputStream.closeEntry();

} catch (IOException e) {

e.printStackTrace();

}

return FileVisitResult.CONTINUE;

}

});

outputStream.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

  • Another easy one is Apache Commons Inbuild Library to write file to a specific directory, then use ZipOutputStream to finally Zip it.

Sample Code :

FileUtils.copyInputStreamToFile(inputStream, folderLocationToPlaceTheFile);

  • Common thing here would be to use ZipOutputStream, to finally zip the entire directory.
  • Writing and reading files involved IO operation, which would be little slower, depending how many files we need to read / write.

 

Approach 2 :

  • Suppose say as in the scenario described in approach 1, if we are getting file from an Objectstore (S3, Apache Minio, DB ..) and we are getting one file at a time, instead of writing everything to file system if we can zip the file in Memory would be lot faster.
  • From Memory Consumption point of view, files should definetly not be huge, in one of the scenario I worked on, we needed to zip pdf files size hardly in 10’s of KB, so even if anypoint we were getting 1000’s of PDF for a particular client, we would not be even going to 100 MB (Load Testing Result), in this scenario, performance was more important and with different system interacting (as in our case) we decided to go with In Memory Zipping of the file.

Code Sample :

// In the below example, JavaPojoObject is a simple Pojo class, having relevant details

// about the filename, storeLocation etc. I feel this convenient to play around by

// creating list which could easily converted to Json back to Java or any other format.

try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {

try (ZipOutputStream zos = new ZipOutputStream(baos);) {

for (JavaPojoObject obj : javaPojoObjectList) {

String filename = obj.getFileName();

byte[] fileContent = yourImplementation

ToGetFileFromYourObjectStore();

ZipEntry individualEntry = new ZipEntry(filename);

individualEntry.setSize(fileContent.length);

zos.putNextEntry(individualEntry);

zos.write(fileContent);

zos.closeEntry();

}

 

}

// baos – now has the entire zip file content, wisely do things you need to ..

}

 

Rabbit MQ Setup and SSL Connectivity with Mule

Parallel Processing

There are different ways in which we can achieve this, but my personal favorite is using some kind of external Queuing Mechanism, which will help us do the processing at all the Server node levels within a Cluster (We should have a Cluster setup, and in Enterprise world that would always be the case).

 

Although Mule has its own VM Queue, it’s in Memory, and could be used for say Reliability Pattern or Inter Flow Communication, basically we need to make sure that there is not huge data we are trying to store in VM Queues, and if we are really want to do parallel processing for Heavy Objects and just for the topping it takes time for processing (This comes with the flavor of our Architecture as we need to make sure that our system when doing Parallel Processing is Fault Tolerant and we cannot lose a single message, means we need to release a message from Queue only when we want to – Will talk about the entire Design of Highly Scalable System in another post).

 

Here, we will be writing only about the setup of RabbitMQ and how to use it in Mule.

 

Rabbit MQ – SSL Connectivity with Mule.

 

  1. First we need to have a Rabbit MQ Server with SSL and TLS enabled.
  2. Follow this site from rabbit mq to set up the server accordingly.

Refrence : https://www.rabbitmq.com/ssl.html

Nice Article depicting the same : http://yuanmengblog.blogspot.com/2015/08/use-mule-amqps-ssl-to-connect-to.html

 

  1. Once the Certs are installed on Rabbit MQ Server, we will have to tell Rabbit MQ, use this SSL and allow connectivity only if Request is coming with a valid .jks and .p12 file.
  2. We can enable this by going to /etc/rabbitmq/conf

Enable below option, ssl_options.fail_if_no_peer_cert = true

 

Complete Configuration, related to SSL.

 

## TLS configuration.

##

## Related doc guide: http://rabbitmq.com/ssl.html.

##

 

ssl_options.cacertfile = /etc/rabbitmq/testca/cacert.pem

ssl_options.certfile = /etc/rabbitmq/server/cert.pem

ssl_options.keyfile = /etc/rabbitmq/server/key.pem

ssl_options.verify = verify_peer

ssl_options.fail_if_no_peer_cert = true

 

ssl_options.versions.1 = tlsv1.2

ssl_options.versions.2 = tlsv1.1

 

Steps to Set up Mule to Connect to Rabbit MQ with SSL Enabled

  1. Generate Truststore using .pem file from Server (we will have .pem in client and test ca as well, we need to select server file here).
  2. Use .p12 file from client, and associated password which should be provided if you were not the one who set up SSL on Rabbit MQ Server, if you yourself did the SSL Set up on Rabbit MQ, you know it .

 

Example:

<amqps:connector name=”AMQPS_0_9_Connector1″ validateConnections=”true” host=”10.20.30.40″ username=”app_admin” password=”somepassword” doc:name=”AMQPS-0-9 Connector”>

<amqps:ssl-key-store path=”/Users/vashists/Documents/rabbitmq-ssl-ananth/rabbit-ssl-config/client/keycert.p12″ keyPassword=”MySecretPassword” storePassword=”MySecretPassword” type=”pkcs12″ algorithm=”SunX509″/>

<amqps:ssl-trust-store path=”/Users/vashists/Documents/rabbitmq-ssl-ananth/rabbit-ssl-config/trustStore.jks” storePassword=”abc123″ type=”jks” algorithm=”SunX509″/>

</amqps:connector>

 

 

 

Reload Mule App Properties without Restarting or RedeployingApp

This is something which I liked, as we had the requirement (not discussing the requirement here) where in Admin Team will change the Mule Properties file, and Running Application should be able to pick this up at Runtime.

Basic Idea here is, to load property from Spring Bean, as all the SpringBeans will be available in Mule Registry, so if we need to reload, at regular time (Poll Mechanism) we should be able to read the same Spring Bean and update the Mule Registry.

Step 1: Define Spring Bean

<spring:bean id=“PropertyFileReader” name=“PropertyFileReader” class=“com.util.PropertyFileReader” scope=“singleton”/>

Step 2:

Java Code to Read Properties file

Step 3:

Poll Component to poll at regualar intervals, this flow will invoke the Java Class defined in Step 2.

New Java Component will call Java Class defined in Step 2, and will set it in registry.

This is it !!!

Only thing we need to understand is how Mule handles Spring in Mule Registry, so basically we are just playing with the Bean already registered in Mule Registry.

Mule Start / Stop Service Customization

Mule Start / Stop Service Customization with lots of Flexibility.

 

Below concept can be used in on-prem or hybrid Mule setup.

 

Typically when we need to start or stop mule we can go in the “bin” directory of mule and do ./start mule or ./stop mule.

Now if say we had some environment variable like our env=”dev, qa, uat, prd ” etc. we can go to our wrapper.conf file and add those entries which is one good way.

Tomorrow for some reason we have to disregard entire Mule Server or Cluster of Servers probably its corrupt (rare case, but it has happened in the company I was working), or say we are upgrading the version (essentially means new runtime).

While setting up this kinds of things, we will have to recollect everything that was done in wrapper.conf file of our Mule Runtime Installation, as all the values are very critical. How about doing this dynamically ?

 

More importantly our Mule Runtime is in AWS EC2 instance, and we need to allocate sufficient Memory to our Mule RT Servers, again we can go to wrapper.conf and do the changes there, but say we have an environment where we are still in evolving phase or say new heavy applications are getting added, we might have to increase the Server Memory, or say in Dev Environment we have not done any optimization and our box size is small comparted to QA/UAT and Prd, we can set the Memory value individually in wrapper.conf file of respective Mule RT Servers.

It would be better idea if we are dynamically setting the Memory size based on the machine’s Memory, or more over what is the free memory that is available in the box ? I guess if we can do this we are reducing a lot of manual effort DevOps would be very happy and at the same time as an Architect our life would be good J

 

Use of systemctl Command to achieve this.

NOTE : Use this with caution, as it is very powerful read more about this how systemctl uses systemd System and all the Services, also you got to have sudo access to do this.

So what we are going to do here is create a Mule Service which can be identified via systemctl.

 

  1. Location of the system directory : /etc/systemd/system
  2. Create a file service (this file name is what we will be using in systemctl command to do our required operations, please note we can either user either full name “mule.service” or shorter form “mule” .service is required in creating file but not in systemctl command).
  3. Below is the code, I have just created a separate file “sh” to keep “mule.service” file short for better understanding.

[Unit]

Description=Mule Runtime Service // Meaningful Description which can be used by Systemctl commands if we ever list all the services running on our System or something else.

After=network-online.target // Would be executed only after the network is started

 

[Service]

Type=forking

User=mule      // which user will have access and who can start / stop Mule Runtime Servers

Group=mule    // All the required users should belong to this Group

ExecStart=/var/lib/mule/mule.sh start         // this is the file where we have written all the customization before we start the mule, which you can go ahead and customize it as per your need

ExecStop=/var/lib/mule/mule.sh stop          // same as above for stop command any customization needed

StandardOutput=syslog                                  // standard output location

StandardError=syslog                                     // standard error location

SyslogIdentifier=mule

 

[Install]

WantedBy=multi-user.target                         // will be needed by multiple user to access this service

 

 

 

 

 

  1. Now for mule.sh file, we need to learn and understand atleast basics of shell programming, and below is snippet of the code.

#!/bin/bash

# RHEL Mule Init Script

echo “Mule init script run as $(whoami)”

 

# Set JDK related environment

JAVA_HOME=/usr/lib/jvm/jre-openjdk

PATH=$PATH:$JAVA_HOME/bin

 

# Set Mule related environment

mule_home=”/var/lib/mule/mule-enterprise-standalone-3.9.0″

mule_wrapper=$mule_home/conf/wrapper.conf

 

MULE_ENV=production

# Export environment variables

export JAVA_HOME MULE_HOME MULE_LIB PATH MULE_ENV RUN_AS_USER

 

memsize=$(free -m | grep Mem | awk ‘{print $2}’)

memsize_KB=$(cat /proc/meminfo | grep MemTotal | awk  ‘{print $2}’)

memsize_GB=$((memsize_KB/1000000 + 1))

action=$1

cd $mule_home/bin

 

if [[ $memsize_GB -ge 32 ]]; then

heapsize_MB=$((memsize_GB*1000/2))

elif [[ $memsize_GB -ge 24 ]]; then

heapsize_MB=16000

elif [[ $memsize_GB -ge 16 ]]; then

heapsize_MB=12000

elif [[ $memsize_GB -ge 12 ]]; then

heapsize_MB=8000

elif [[ $memsize_GB -ge 8 ]]; then

heapsize_MB=4000

else

heapsize_MB=1024

fi

 

case “$action” in

start|restart)

echo “setting Mule heapsize to $heapsize_MB MB”

sed -i s/^wrapper.java.initmemory=.*/wrapper.java.initmemory=$heapsize_MB/ $mule_wrapper

sed -i s/^wrapper.java.maxmemory=.*/wrapper.java.initmemory=$heapsize_MB/ $mule_wrapper

;;

*)

;;

esac

 

case “$action” in

start)

echo “Start service mule”

$mule_home/bin/mule start

;;

stop)

echo “Stop service mule”

$mule_home/bin/mule stop

;;

restart)

echo “Restart service mule”

$mule_home/bin/mule restart

;;

status)

echo “Mule status”

$mule_home/bin/mule status

;;

*)

echo “Usage: $0 {start|stop|restart}”

exit 1

;;

esac

 

 

 

  1. Finally add our mule.service file to /etc/systemd/system/multi-user.target.wants directory giving full access.
  2. Finally try doing

systemctl status mule or systemctl start mule, where command is

systemctl “action” “service”

action = status or stop or start

service = mule

Mule Validation Component to validate Inbound Request

There are various ways in which we can validate Inbound Request (both payload body and headers). Ideally if we are creating API’s in mule, we can do lot of boiler plate validation code in RAML File itself (will talk more about RAML and its modularization in another post).

At times we cannot rely on RAML for doing validation, as RAML’s validation will kick in only when API Kit Router is encountered, at times we might have to do our validation before that, or say we do not have the luxury of RAML for some legacy code or an immediate defect raised where we cannot change the architecture of the existing project, or we need to validate few things in the middle of the flows. There could be lot of scenarios.

Lets say we are validation Inbound Request using Mule Validation Component.

Example, its a Post Request with JSON Body and say JWT Token as Authorization Header.

We need to validate

  1. If JSON Body is not empty
  2. If all the attribute is present within the JSON Body, and if any required attribute is missing we should send meaningful message back UI.
  3. If Required Header Parameter (Authorization) in our case is present and if not send meaningful message back to UI.

 

For Validating Header, we can use Validation:all component, and provide multiple checks like

  1. Header should be present (null check)
  2. Since this is JWT Token, we can write regex to validate if we are having 3 parts in the incoming Request token.

<validation:all config-ref=“Validation_Configuration”   doc:name=“Validate Header”>

            <validation:validations>

                <validation:is-not-empty message=“Authorization Header Cannot be Empty” value=“#[message.inboundProperties.’Authorization’]”/>

                <validation:matches-regex message=“Authorization Header is having Incorrect Value” value=“#[message.inboundProperties.’Authorization’]” regex=“[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?”/>

            </validation:validations>

        </validation:all>

To Validate individual key in the JSON Object, we just have to make sure that the incoming request is in the required MIME Type and use another validation:all component like

<validation:all  config-ref=“Validation_Configuration” doc:name=“Validate Request Body Parameters”>

            <validation:validations >

                <validation:is-not-empty value=“#[json:key1]” message=“key1 Parameter is missing”/>

                <validation:is-not-empty value=“#[json:key2]” message=“key2 params is missing”/>

            </validation:validations>

        </validation:all>

This is all good, little tricky part would be when we need to validate an empty payload body, as the payload would be {NullPayload} and we should be able to evaluate this using expression with the code #[payload == null] or #[payload==empty], but when we try to put a validation component of validation:is-not-empty or validation:is-not-null things will behave little differently and we have to make sure that we are actually not doing comparison or trying to evaluate {NullPayload} but instead we should evaluate nullness and then set an empty payload like “{}” and do the validation against it.

Else we might be able to handle few scenarios, but we can get java Exception which is not handled by code and some Runtime Exception would be thrown on UI.

Here is the trick to handle {NullPayload} and get all the validation right with controlled messages in all the scenarios. Below expression converts the string “{}” into an object with the help of dw() expression.

<expression-component doc:name=“Convert Payload”><![CDATA[#[payload != null ? payload : dw({})]]]></expression-component>

And then use validation component like

<validation:is-not-null config-ref=“Validation_Configuration” message=“Request is missing Payload Body” value=“#[payload]” doc:name=“Validation”/>

Obviously first checking the null payload scenario and handling it will make more sense.

Complete code will look something like

<flow name=“validate-input-payloadFlow”>

        <validation:all config-ref=“Validation_Configuration”   doc:name=“Validate Header”>

            <validation:validations>

                <validation:is-not-empty message=“Authorization Header Cannot be Empty” value=“#[message.inboundProperties.’Authorization’]”/>

                <validation:matches-regex message=“Authorization Header is having Incorrect Value” value=“#[message.inboundProperties.’Authorization’]” regex=“[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?”/>

            </validation:validations>

        </validation:all>

        <expression-component doc:name=“Convert Payload”><![CDATA[#[payload != null ? payload : dw({})]]]></expression-component>

        <validation:is-not-null config-ref=“Validation_Configuration” message=“Request is missing Payload Body” value=“#[payload]” doc:name=“Validation”/>

    <validation:all  config-ref=“Validation_Configuration” doc:name=“Validate Request Body Parameters”>

            <validation:validations >

                <validation:is-not-empty value=“#[json:initTime]” message=“initTime Parameter is missing”/>

                <validation:is-not-empty value=“#[json:fileName]” message=“fileName Parameter is missing”/>

                <validation:is-not-empty value=“#[json:data]” message=“Data params is missing”/>

            </validation:validations>

        </validation:all>

    </flow>

 

Valid Input Payload Example :

{
“key1” : “value1”,
“key2” : “value2”
}

Playing with the above input payload you can understand the behaviour.