Sunday, 30 June 2013

Compressing & Decompressing Files Using Java & Zip4j


You must have zipped and unzipped files using utilities such as Win zip or 7-zip,  probably a million times, but what if you needed to do the zipping (or unzipping) from inside your Java application. For one of my projects I had do precisely that. It is no big deal but there are so many libraries to choose from, so it can get a bit confusing. In particular, I was looking for a tool which was
  • Able to compress and decompress easily (duh...)
  • Allows different levels of compression
  • Allows folders to be compressed
  • Allows multiple files to be compressed
  • Allows encryption
Is that too much to ask for ? Nah... you can do just that with a free open source Java library Zip4j. It allows whole folder or multiple files to be compressed together. You can compress the files (or folders) with different compression settings such as 

                         // DEFLATE_LEVEL_ULTRA = maximum compression
// DEFLATE_LEVEL_MAXIMUM
// DEFLATE_LEVEL_NORMAL = normal compression
// DEFLATE_LEVEL_FAST
// DEFLATE_LEVEL_FASTEST = fastest compression

It support AES encryption with the key strengths of 128, 192 and 256 (and of course you could set a password !). Just to show how easy it is to use, I am attaching a example code which compresses a single file, outputs the compression ratio and again decompresses it. (Please note that you may have change the file names/ paths)

The compressing function 


private static void compress(String inputFile,String compressedFile) {
try {
ZipFile zipFile = new ZipFile(compressedFile);
File inputFileH = new File(inputFile);
ZipParameters parameters = new ZipParameters();

// COMP_DEFLATE is for compression
        // COMp_STORE no compression
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
// DEFLATE_LEVEL_ULTRA = maximum compression
// DEFLATE_LEVEL_MAXIMUM
// DEFLATE_LEVEL_NORMAL = normal compression
// DEFLATE_LEVEL_FAST
// DEFLATE_LEVEL_FASTEST = fastest compression
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_ULTRA);

// file compressed
zipFile.addFile(inputFileH, parameters);
long uncompressedSize = inputFileH.length();
File outputFileH = new File(compressedFile);
long comrpessedSize = outputFileH.length();
//System.out.println("Size "+uncompressedSize+" vs "+comrpessedSize);
double ratio = (double)comrpessedSize/(double)uncompressedSize;
System.out.println("File compressed with compression ratio : "+ ratio);

} catch (Exception e) {
e.printStackTrace();
}
}

The decompressing function 

public static void decompress(String compressedFile,String destination) {
try {
   ZipFile zipFile = new ZipFile(compressedFile);
   if (zipFile.isEncrypted()) {
       //zipFile.setPassword(password);
   }
   zipFile.extractAll(destination);
} catch (ZipException e) {
   e.printStackTrace();
}

System.out.println("File Decompressed");
}

And the main function 

public static void main(String[] args) {


String filePath = ADD THE FILE PATH
String fileName = ADD THE FILENAME
String inputFile = filePath + fileName;

Calendar calendar = Calendar.getInstance();
long millisecs = calendar.getTime().getTime();

// compressed file name
String compressedFile = inputFile + "_" + millisecs + ".zip";
//invoke file compression
compress(inputFile,compressedFile);

millisecs = calendar.getTime().getTime();
String destination = filePath + "decompress";

decompress(compressedFile,destination);

}

Also you would need to add the following libraries to the top and add the zip4j jar to your build path.

import java.io.File;
import java.util.Calendar;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;


Happy (Un)zipping ;-

Monday, 17 June 2013

           
Extracting basic blocks information from Java Bytecodes
   
I was looking for some way to extract the basic block information from Java bytecodes, when I found the Javassist library. According to the Javassist website


Following is simple example, showing how to use it. Our objective here is to find basic block information in an method, in a given class file. A basic block can be described as a portion of code with a single entry and exit point. This implies that if the execution enters a basic block, all the instruction in that basic block would be executed.

I downloaded Javassist from the following website :
http://sourceforge.net/projects/jboss/files/Javassist/3.18.0-GA/

So first we need a class, which we wish to study. I used the following class file.



public class SampleCode {

    private int varA;
    private int varB;
    private int toggle;
    private int result;
   
    public SampleCode() {
        super();
        this.varA = 0;
        this.varB = 0;
        this.toggle = 0;
        this.result = -1;
    }

    public void setVarA(int varA) {
        this.varA = varA;
    }

    public void setVarB(int varB) {
        this.varB = varB;
    }

    public void setToggle(int toggle) {
        this.toggle = toggle;
    }
   
    public int calculate(){       
        if(toggle==1){
            result = varA * varA + varB;
        }else{
            result = varA + varB;
        }
        return result;
    }
}




Now to extract the basic block, we can use the following class file


import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.analysis.ControlFlow;
import javassist.bytecode.analysis.ControlFlow.Block;
import static javassist.ClassPool.getDefault;

public class BasicBlockExtractor {

    public static final String CLASSNAME = "SampleCode";
    public static final String METHODNAME_1 = "calculate";
    public static final String METHODNAME_2 = "setVarA";

    public static void main(String[] args) throws NotFoundException, BadBytecode {

        extractBasicBlocks();

    }
   
    public static void extractBasicBlocks() throws NotFoundException, BadBytecode{
        //Get the Class implementation byte code
           CtClass ctClass = getDefault().get(CLASSNAME);

           //Get the method from the Class byte code
           CtMethod ctmethod = ctClass.getDeclaredMethod(METHODNAME_1);
          
           ControlFlow cf = new ControlFlow(ctmethod);
           Block[] blocks = cf.basicBlocks();
          
           System.out.println("Number of basic blocks in class ' "+ CLASSNAME + " ' in method ' " + METHODNAME_1 + " ' is " + blocks.length);

          
    }
}



You must add the Javassist jar file to you project, for this to work. Execution the above code gives the following output 

"Number of basic blocks in class ' SampleCode ' in method ' calculate ' is 4"

Subsequently, if you wish to obtain the line numbers for different basic blocks in a method you could do it as follows :-


import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.analysis.ControlFlow;
import javassist.bytecode.analysis.ControlFlow.Block;
import static javassist.ClassPool.getDefault;

public class BasicBlockExtractor{

    public static final String CLASSNAME = "SampleCode";
    public static final String METHODNAME_1 = "calculate";
    public static final String METHODNAME_2 = "setVarA";

    public static void main(String[] args) throws NotFoundException, BadBytecode, CannotCompileException {

        extractBasicBlocks();

    }
   
    public static void extractBasicBlocks() throws NotFoundException, BadBytecode, CannotCompileException{
        //Get the Class implementation byte code
           CtClass ctClass = getDefault().get(CLASSNAME);

           //Get the method from the Class byte code
           CtMethod ctmethod = ctClass.getDeclaredMethod(METHODNAME_1);
          
           ControlFlow cf = new ControlFlow(ctmethod);
           Block[] blocks = cf.basicBlocks();
          
           System.out.println("Number of basic blocks in class ' "+ CLASSNAME + " ' in method ' " + METHODNAME_1 + " ' is " + blocks.length);

           for(Block thisbb : blocks){
               int pos=ctmethod.getMethodInfo().getLineNumber(thisbb.position()); //Source code line position from binary line position
               System.out.println("Basic block "+thisbb.index() +" is at "+pos);
           }
    }
}




For me, the output of the above code was as follows :-

Number of basic blocks in class ' SampleCode ' in method ' calculate ' is 4
Basic block 0 is at 35
Basic block 1 is at 36
Basic block 2 is at 38
Basic block 3 is at 41